#!/usr/bin/python
# -*- coding: utf-8 -*-
# Description: pefile
"""pefile, Portable Executable reader module

All the PE file basic structures are available with their default names as attributes of the instance returned.

Processed elements such as the import table are made available with lowercase names, to differentiate them from the upper case basic structure names.

pefile has been tested against the limits of valid PE headers, that is, malware.
Lots of packed malware attempt to abuse the format way beyond its standard use.
To the best of my knowledge most of the abuses are handled gracefully.
Copyright (c) 2005, 2006, 2007, 2008 Ero Carrera <ero@dkbza.org> All rights reserved.

For detailed copyright information see the file COPYING in the root of the distribution archive.
"""

__author__ = u'Ero Carrera, fixed by mk2(fengmk2@gmail.com)'
__version__ = u'1.2.9.1.1'
__contact__ = u'ero@dkbza.org'

import os
import sys
import struct
import time
import math
import re
import exceptions
import string
import array
from threading import Timer

sha1, sha256, sha512, md5 = None, None, None, None

try:
    import hashlib
    sha1 = hashlib.sha1
    sha256 = hashlib.sha256
    sha512 = hashlib.sha512
    md5 = hashlib.md5
except ImportError:    
    try:
        import sha
        sha1 = sha.new
    except ImportError:
        pass
    try:
        import md5
        md5 = md5.new
    except ImportError:
        pass
import asn1
import signeddata

IMAGE_DOS_SIGNATURE             = 0x5A4D
IMAGE_OS2_SIGNATURE             = 0x454E
IMAGE_OS2_SIGNATURE_LE          = 0x454C
IMAGE_VXD_SIGNATURE             = 0x454C
IMAGE_NT_SIGNATURE              = 0x00004550
IMAGE_NUMBEROF_DIRECTORY_ENTRIES= 16
IMAGE_ORDINAL_FLAG              = 0x80000000L
IMAGE_ORDINAL_FLAG64            = 0x8000000000000000L
OPTIONAL_HEADER_MAGIC_PE        = 0x10b
OPTIONAL_HEADER_MAGIC_PE_PLUS   = 0x20b

directory_entry_types = [
    (u'IMAGE_DIRECTORY_ENTRY_EXPORT',        0),
    (u'IMAGE_DIRECTORY_ENTRY_IMPORT',        1),
    (u'IMAGE_DIRECTORY_ENTRY_RESOURCE',      2),
    (u'IMAGE_DIRECTORY_ENTRY_EXCEPTION',     3),
    (u'IMAGE_DIRECTORY_ENTRY_SECURITY',      4),
    (u'IMAGE_DIRECTORY_ENTRY_BASERELOC',     5),
    (u'IMAGE_DIRECTORY_ENTRY_DEBUG',         6),
    (u'IMAGE_DIRECTORY_ENTRY_COPYRIGHT',     7),
    (u'IMAGE_DIRECTORY_ENTRY_GLOBALPTR',     8),
    (u'IMAGE_DIRECTORY_ENTRY_TLS',           9),
    (u'IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG',   10),
    (u'IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT',  11),
    (u'IMAGE_DIRECTORY_ENTRY_IAT',           12),
    (u'IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT',  13),
    (u'IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR',14),
    (u'IMAGE_DIRECTORY_ENTRY_RESERVED',      15) ]

DIRECTORY_ENTRY = dict([(e[1], e[0]) for e in directory_entry_types]+directory_entry_types)

image_characteristics = [
    (u'IMAGE_FILE_RELOCS_STRIPPED',          0x0001),
    (u'IMAGE_FILE_EXECUTABLE_IMAGE',         0x0002),
    (u'IMAGE_FILE_LINE_NUMS_STRIPPED',       0x0004),
    (u'IMAGE_FILE_LOCAL_SYMS_STRIPPED',      0x0008),
    (u'IMAGE_FILE_AGGRESIVE_WS_TRIM',        0x0010),
    (u'IMAGE_FILE_LARGE_ADDRESS_AWARE',      0x0020),
    (u'IMAGE_FILE_16BIT_MACHINE',            0x0040),
    (u'IMAGE_FILE_BYTES_REVERSED_LO',        0x0080),
    (u'IMAGE_FILE_32BIT_MACHINE',            0x0100),
    (u'IMAGE_FILE_DEBUG_STRIPPED',           0x0200),
    (u'IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP',  0x0400),
    (u'IMAGE_FILE_NET_RUN_FROM_SWAP',        0x0800),
    (u'IMAGE_FILE_SYSTEM',                   0x1000),
    (u'IMAGE_FILE_DLL',                      0x2000),
    (u'IMAGE_FILE_UP_SYSTEM_ONLY',           0x4000),
    (u'IMAGE_FILE_BYTES_REVERSED_HI',        0x8000) ]

IMAGE_CHARACTERISTICS = dict([(e[1], e[0]) for e in
    image_characteristics]+image_characteristics)

section_characteristics = [
    (u'IMAGE_SCN_CNT_CODE',                  0x00000020),
    (u'IMAGE_SCN_CNT_INITIALIZED_DATA',      0x00000040),
    (u'IMAGE_SCN_CNT_UNINITIALIZED_DATA',    0x00000080),
    (u'IMAGE_SCN_LNK_OTHER',                 0x00000100),
    (u'IMAGE_SCN_LNK_INFO',                  0x00000200),
    (u'IMAGE_SCN_LNK_REMOVE',                0x00000800),
    (u'IMAGE_SCN_LNK_COMDAT',                0x00001000),
    (u'IMAGE_SCN_MEM_FARDATA',               0x00008000),
    (u'IMAGE_SCN_MEM_PURGEABLE',             0x00020000),
    (u'IMAGE_SCN_MEM_16BIT',                 0x00020000),
    (u'IMAGE_SCN_MEM_LOCKED',                0x00040000),
    (u'IMAGE_SCN_MEM_PRELOAD',               0x00080000),
    (u'IMAGE_SCN_ALIGN_1BYTES',              0x00100000),
    (u'IMAGE_SCN_ALIGN_2BYTES',              0x00200000),
    (u'IMAGE_SCN_ALIGN_4BYTES',              0x00300000),
    (u'IMAGE_SCN_ALIGN_8BYTES',              0x00400000),
    (u'IMAGE_SCN_ALIGN_16BYTES',             0x00500000),
    (u'IMAGE_SCN_ALIGN_32BYTES',             0x00600000),
    (u'IMAGE_SCN_ALIGN_64BYTES',             0x00700000),
    (u'IMAGE_SCN_ALIGN_128BYTES',            0x00800000),
    (u'IMAGE_SCN_ALIGN_256BYTES',            0x00900000),
    (u'IMAGE_SCN_ALIGN_512BYTES',            0x00A00000),
    (u'IMAGE_SCN_ALIGN_1024BYTES',           0x00B00000),
    (u'IMAGE_SCN_ALIGN_2048BYTES',           0x00C00000),
    (u'IMAGE_SCN_ALIGN_4096BYTES',           0x00D00000),
    (u'IMAGE_SCN_ALIGN_8192BYTES',           0x00E00000),
    (u'IMAGE_SCN_ALIGN_MASK',                0x00F00000),
    (u'IMAGE_SCN_LNK_NRELOC_OVFL',           0x01000000),
    (u'IMAGE_SCN_MEM_DISCARDABLE',           0x02000000),
    (u'IMAGE_SCN_MEM_NOT_CACHED',            0x04000000),
    (u'IMAGE_SCN_MEM_NOT_PAGED',             0x08000000),
    (u'IMAGE_SCN_MEM_SHARED',                0x10000000),
    (u'IMAGE_SCN_MEM_EXECUTE',               0x20000000),
    (u'IMAGE_SCN_MEM_READ',                  0x40000000),
    (u'IMAGE_SCN_MEM_WRITE',                 0x80000000L) ]
 
SECTION_CHARACTERISTICS = dict([(e[1], e[0]) for e in
                                        section_characteristics]+section_characteristics)

debug_types = [
    (u'IMAGE_DEBUG_TYPE_UNKNOWN',        0),
    (u'IMAGE_DEBUG_TYPE_COFF',           1),
    (u'IMAGE_DEBUG_TYPE_CODEVIEW',       2),
    (u'IMAGE_DEBUG_TYPE_FPO',            3),
    (u'IMAGE_DEBUG_TYPE_MISC',           4),
    (u'IMAGE_DEBUG_TYPE_EXCEPTION',      5),
    (u'IMAGE_DEBUG_TYPE_FIXUP',          6),
    (u'IMAGE_DEBUG_TYPE_OMAP_TO_SRC',    7),
    (u'IMAGE_DEBUG_TYPE_OMAP_FROM_SRC',  8),
    (u'IMAGE_DEBUG_TYPE_BORLAND',        9),
    (u'IMAGE_DEBUG_TYPE_RESERVED10',     10) ]

DEBUG_TYPE = dict([(e[1], e[0]) for e in debug_types]+debug_types)

subsystem_types = [
    (u'IMAGE_SUBSYSTEM_UNKNOWN',     0),
    (u'IMAGE_SUBSYSTEM_NATIVE',      1),
    (u'IMAGE_SUBSYSTEM_WINDOWS_GUI', 2),
    (u'IMAGE_SUBSYSTEM_WINDOWS_CUI', 3),
    (u'IMAGE_SUBSYSTEM_OS2_CUI',     5),
    (u'IMAGE_SUBSYSTEM_POSIX_CUI',   7),
    (u'IMAGE_SUBSYSTEM_WINDOWS_CE_GUI',  9),
    (u'IMAGE_SUBSYSTEM_EFI_APPLICATION', 10),
    (u'IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER', 11),
    (u'IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER',      12),
    (u'IMAGE_SUBSYSTEM_EFI_ROM',     13),
    (u'IMAGE_SUBSYSTEM_XBOX',        14)]

SUBSYSTEM_TYPE = dict([(e[1], e[0]) for e in subsystem_types]+subsystem_types)

machine_types = [
    (u'IMAGE_FILE_MACHINE_UNKNOWN',  0),
    (u'IMAGE_FILE_MACHINE_AM33',     0x1d3),
    (u'IMAGE_FILE_MACHINE_AMD64',    0x8664),
    (u'IMAGE_FILE_MACHINE_ARM',      0x1c0),
    (u'IMAGE_FILE_MACHINE_EBC',      0xebc),
    (u'IMAGE_FILE_MACHINE_I386',     0x14c),
    (u'IMAGE_FILE_MACHINE_IA64',     0x200),
    (u'IMAGE_FILE_MACHINE_MR32',     0x9041),
    (u'IMAGE_FILE_MACHINE_MIPS16',   0x266),
    (u'IMAGE_FILE_MACHINE_MIPSFPU',  0x366),
    (u'IMAGE_FILE_MACHINE_MIPSFPU16',0x466),
    (u'IMAGE_FILE_MACHINE_POWERPC',  0x1f0),
    (u'IMAGE_FILE_MACHINE_POWERPCFP',0x1f1),
    (u'IMAGE_FILE_MACHINE_R4000',    0x166),
    (u'IMAGE_FILE_MACHINE_SH3',      0x1a2),
    (u'IMAGE_FILE_MACHINE_SH3DSP',   0x1a3),
    (u'IMAGE_FILE_MACHINE_SH4',      0x1a6),
    (u'IMAGE_FILE_MACHINE_SH5',      0x1a8),
    (u'IMAGE_FILE_MACHINE_THUMB',    0x1c2),
    (u'IMAGE_FILE_MACHINE_WCEMIPSV2',0x169),
 ]

MACHINE_TYPE = dict([(e[1], e[0]) for e in machine_types]+machine_types)

relocation_types = [
    (u'IMAGE_REL_BASED_ABSOLUTE',        0),
    (u'IMAGE_REL_BASED_HIGH',            1),
    (u'IMAGE_REL_BASED_LOW',             2),
    (u'IMAGE_REL_BASED_HIGHLOW',         3),
    (u'IMAGE_REL_BASED_HIGHADJ',         4),
    (u'IMAGE_REL_BASED_MIPS_JMPADDR',    5),
    (u'IMAGE_REL_BASED_SECTION',         6),
    (u'IMAGE_REL_BASED_REL',             7),
    (u'IMAGE_REL_BASED_MIPS_JMPADDR16',  9),
    (u'IMAGE_REL_BASED_IA64_IMM64',      9),
    (u'IMAGE_REL_BASED_DIR64',           10),
    (u'IMAGE_REL_BASED_HIGH3ADJ',        11) ]

RELOCATION_TYPE = dict([(e[1], e[0]) for e in relocation_types]+relocation_types)

dll_characteristics = [
    (u'IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0001', 0x0001),
    (u'IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0002', 0x0002),
    (u'IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0004', 0x0004),
    (u'IMAGE_DLL_CHARACTERISTICS_RESERVED_0x0008', 0x0008),
    (u'IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE',      0x0040),
    (u'IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY',   0x0080),
    (u'IMAGE_DLL_CHARACTERISTICS_NX_COMPAT',         0x0100),
    (u'IMAGE_DLL_CHARACTERISTICS_NO_ISOLATION',      0x0200),
    (u'IMAGE_DLL_CHARACTERISTICS_NO_SEH',    0x0400),
    (u'IMAGE_DLL_CHARACTERISTICS_NO_BIND',   0x0800),
    (u'IMAGE_DLL_CHARACTERISTICS_RESERVED_0x1000', 0x1000),
    (u'IMAGE_DLL_CHARACTERISTICS_WDM_DRIVER',    0x2000),
    (u'IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE', 0x8000) ]

DLL_CHARACTERISTICS = dict([(e[1], e[0]) for e in dll_characteristics]+dll_characteristics)

# Resource types
resource_type = [
    (u'RT_CURSOR',          1),
    (u'RT_BITMAP',          2),
    (u'RT_ICON',            3),
    (u'RT_MENU',            4),
    (u'RT_DIALOG',          5),
    (u'RT_STRING',          6),
    (u'RT_FONTDIR',         7),
    (u'RT_FONT',            8),
    (u'RT_ACCELERATOR',     9),
    (u'RT_RCDATA',          10),
    (u'RT_MESSAGETABLE',    11),
    (u'RT_GROUP_CURSOR',    12),
    (u'RT_GROUP_ICON',      14),
    (u'RT_VERSION',         16),
    (u'RT_DLGINCLUDE',      17),
    (u'RT_PLUGPLAY',        19),
    (u'RT_VXD',             20),
    (u'RT_ANICURSOR',       21),
    (u'RT_ANIICON',         22),
    (u'RT_HTML',            23),
    (u'RT_MANIFEST',        24) ]

RESOURCE_TYPE = dict([(e[1], e[0]) for e in resource_type]+resource_type)

    
# Language definitions
lang = [
 (u'LANG_NEUTRAL',       0x00),
 (u'LANG_INVARIANT',     0x7f),
 (u'LANG_AFRIKAANS',     0x36),
 (u'LANG_ALBANIAN',      0x1c),
 (u'LANG_ARABIC',        0x01),
 (u'LANG_ARMENIAN',      0x2b),
 (u'LANG_ASSAMESE',      0x4d),
 (u'LANG_AZERI',         0x2c),
 (u'LANG_BASQUE',        0x2d),
 (u'LANG_BELARUSIAN',    0x23),
 (u'LANG_BENGALI',       0x45),
 (u'LANG_BULGARIAN',     0x02),
 (u'LANG_CATALAN',       0x03),
 (u'LANG_CHINESE',       0x04),
 (u'LANG_CROATIAN',      0x1a),
 (u'LANG_CZECH',         0x05),
 (u'LANG_DANISH',        0x06),
 (u'LANG_DIVEHI',        0x65),
 (u'LANG_DUTCH',         0x13),
 (u'LANG_ENGLISH',       0x09),
 (u'LANG_ESTONIAN',      0x25),
 (u'LANG_FAEROESE',      0x38),
 (u'LANG_FARSI',         0x29),
 (u'LANG_FINNISH',       0x0b),
 (u'LANG_FRENCH',        0x0c),
 (u'LANG_GALICIAN',      0x56),
 (u'LANG_GEORGIAN',      0x37),
 (u'LANG_GERMAN',        0x07),
 (u'LANG_GREEK',         0x08),
 (u'LANG_GUJARATI',      0x47),
 (u'LANG_HEBREW',        0x0d),
 (u'LANG_HINDI',         0x39),
 (u'LANG_HUNGARIAN',     0x0e),
 (u'LANG_ICELANDIC',     0x0f),
 (u'LANG_INDONESIAN',    0x21),
 (u'LANG_ITALIAN',       0x10),
 (u'LANG_JAPANESE',      0x11),
 (u'LANG_KANNADA',       0x4b),
 (u'LANG_KASHMIRI',      0x60),
 (u'LANG_KAZAK',         0x3f),
 (u'LANG_KONKANI',       0x57),
 (u'LANG_KOREAN',        0x12),
 (u'LANG_KYRGYZ',        0x40),
 (u'LANG_LATVIAN',       0x26),
 (u'LANG_LITHUANIAN',    0x27),
 (u'LANG_MACEDONIAN',    0x2f),
 (u'LANG_MALAY',         0x3e),
 (u'LANG_MALAYALAM',     0x4c),
 (u'LANG_MANIPURI',      0x58),
 (u'LANG_MARATHI',       0x4e),
 (u'LANG_MONGOLIAN',     0x50),
 (u'LANG_NEPALI',        0x61),
 (u'LANG_NORWEGIAN',     0x14),
 (u'LANG_ORIYA',         0x48),
 (u'LANG_POLISH',        0x15),
 (u'LANG_PORTUGUESE',    0x16),
 (u'LANG_PUNJABI',       0x46),
 (u'LANG_ROMANIAN',      0x18),
 (u'LANG_RUSSIAN',       0x19),
 (u'LANG_SANSKRIT',      0x4f),
 (u'LANG_SERBIAN',       0x1a),
 (u'LANG_SINDHI',        0x59),
 (u'LANG_SLOVAK',        0x1b),
 (u'LANG_SLOVENIAN',     0x24),
 (u'LANG_SPANISH',       0x0a),
 (u'LANG_SWAHILI',       0x41),
 (u'LANG_SWEDISH',       0x1d),
 (u'LANG_SYRIAC',        0x5a),
 (u'LANG_TAMIL',         0x49),
 (u'LANG_TATAR',         0x44),
 (u'LANG_TELUGU',        0x4a),
 (u'LANG_THAI',          0x1e),
 (u'LANG_TURKISH',       0x1f),
 (u'LANG_UKRAINIAN',     0x22),
 (u'LANG_URDU',          0x20),
 (u'LANG_UZBEK',         0x43),
 (u'LANG_VIETNAMESE',    0x2a),
 (u'LANG_GAELIC',        0x3c),
 (u'LANG_MALTESE',       0x3a),
 (u'LANG_MAORI',         0x28),
 (u'LANG_RHAETO_ROMANCE',0x17),
 (u'LANG_SAAMI',         0x3b),
 (u'LANG_SORBIAN',       0x2e),
 (u'LANG_SUTU',          0x30),
 (u'LANG_TSONGA',        0x31),
 (u'LANG_TSWANA',        0x32),
 (u'LANG_VENDA',         0x33),
 (u'LANG_XHOSA',         0x34),
 (u'LANG_ZULU',          0x35),
 (u'LANG_ESPERANTO',     0x8f),
 (u'LANG_WALON',         0x90),
 (u'LANG_CORNISH',       0x91),
 (u'LANG_WELSH',         0x92),
 (u'LANG_BRETON',        0x93) ]

LANG = dict(lang+[(e[1], e[0]) for e in lang])

# Sublanguage definitions
sublang =  [
 (u'SUBLANG_NEUTRAL',                        0x00),
 (u'SUBLANG_DEFAULT',                        0x01),
 (u'SUBLANG_SYS_DEFAULT',                    0x02),
 (u'SUBLANG_ARABIC_SAUDI_ARABIA',            0x01),
 (u'SUBLANG_ARABIC_IRAQ',                    0x02),
 (u'SUBLANG_ARABIC_EGYPT',                   0x03),
 (u'SUBLANG_ARABIC_LIBYA',                   0x04),
 (u'SUBLANG_ARABIC_ALGERIA',                 0x05),
 (u'SUBLANG_ARABIC_MOROCCO',                 0x06),
 (u'SUBLANG_ARABIC_TUNISIA',                 0x07),
 (u'SUBLANG_ARABIC_OMAN',                    0x08),
 (u'SUBLANG_ARABIC_YEMEN',                   0x09),
 (u'SUBLANG_ARABIC_SYRIA',                   0x0a),
 (u'SUBLANG_ARABIC_JORDAN',                  0x0b),
 (u'SUBLANG_ARABIC_LEBANON',                 0x0c),
 (u'SUBLANG_ARABIC_KUWAIT',                  0x0d),
 (u'SUBLANG_ARABIC_UAE',                     0x0e),
 (u'SUBLANG_ARABIC_BAHRAIN',                 0x0f),
 (u'SUBLANG_ARABIC_QATAR',                   0x10),
 (u'SUBLANG_AZERI_LATIN',                    0x01),
 (u'SUBLANG_AZERI_CYRILLIC',                 0x02),
 (u'SUBLANG_CHINESE_TRADITIONAL',            0x01),
 (u'SUBLANG_CHINESE_SIMPLIFIED',             0x02),
 (u'SUBLANG_CHINESE_HONGKONG',               0x03),
 (u'SUBLANG_CHINESE_SINGAPORE',              0x04),
 (u'SUBLANG_CHINESE_MACAU',                  0x05),
 (u'SUBLANG_DUTCH',                          0x01),
 (u'SUBLANG_DUTCH_BELGIAN',                  0x02),
 (u'SUBLANG_ENGLISH_US',                     0x01),
 (u'SUBLANG_ENGLISH_UK',                     0x02),
 (u'SUBLANG_ENGLISH_AUS',                    0x03),
 (u'SUBLANG_ENGLISH_CAN',                    0x04),
 (u'SUBLANG_ENGLISH_NZ',                     0x05),
 (u'SUBLANG_ENGLISH_EIRE',                   0x06),
 (u'SUBLANG_ENGLISH_SOUTH_AFRICA',           0x07),
 (u'SUBLANG_ENGLISH_JAMAICA',                0x08),
 (u'SUBLANG_ENGLISH_CARIBBEAN',              0x09),
 (u'SUBLANG_ENGLISH_BELIZE',                 0x0a),
 (u'SUBLANG_ENGLISH_TRINIDAD',               0x0b),
 (u'SUBLANG_ENGLISH_ZIMBABWE',               0x0c),
 (u'SUBLANG_ENGLISH_PHILIPPINES',            0x0d),
 (u'SUBLANG_FRENCH',                         0x01),
 (u'SUBLANG_FRENCH_BELGIAN',                 0x02),
 (u'SUBLANG_FRENCH_CANADIAN',                0x03),
 (u'SUBLANG_FRENCH_SWISS',                   0x04),
 (u'SUBLANG_FRENCH_LUXEMBOURG',              0x05),
 (u'SUBLANG_FRENCH_MONACO',                  0x06),
 (u'SUBLANG_GERMAN',                         0x01),
 (u'SUBLANG_GERMAN_SWISS',                   0x02),
 (u'SUBLANG_GERMAN_AUSTRIAN',                0x03),
 (u'SUBLANG_GERMAN_LUXEMBOURG',              0x04),
 (u'SUBLANG_GERMAN_LIECHTENSTEIN',           0x05),
 (u'SUBLANG_ITALIAN',                        0x01),
 (u'SUBLANG_ITALIAN_SWISS',                  0x02),
 (u'SUBLANG_KASHMIRI_SASIA',                 0x02),
 (u'SUBLANG_KASHMIRI_INDIA',                 0x02),
 (u'SUBLANG_KOREAN',                         0x01),
 (u'SUBLANG_LITHUANIAN',                     0x01),
 (u'SUBLANG_MALAY_MALAYSIA',                 0x01),
 (u'SUBLANG_MALAY_BRUNEI_DARUSSALAM',        0x02),
 (u'SUBLANG_NEPALI_INDIA',                   0x02),
 (u'SUBLANG_NORWEGIAN_BOKMAL',               0x01),
 (u'SUBLANG_NORWEGIAN_NYNORSK',              0x02),
 (u'SUBLANG_PORTUGUESE',                     0x02),
 (u'SUBLANG_PORTUGUESE_BRAZILIAN',           0x01),
 (u'SUBLANG_SERBIAN_LATIN',                  0x02),
 (u'SUBLANG_SERBIAN_CYRILLIC',               0x03),
 (u'SUBLANG_SPANISH',                        0x01),
 (u'SUBLANG_SPANISH_MEXICAN',                0x02),
 (u'SUBLANG_SPANISH_MODERN',                 0x03),
 (u'SUBLANG_SPANISH_GUATEMALA',              0x04),
 (u'SUBLANG_SPANISH_COSTA_RICA',             0x05),
 (u'SUBLANG_SPANISH_PANAMA',                 0x06),
 (u'SUBLANG_SPANISH_DOMINICAN_REPUBLIC',     0x07),
 (u'SUBLANG_SPANISH_VENEZUELA',              0x08),
 (u'SUBLANG_SPANISH_COLOMBIA',               0x09),
 (u'SUBLANG_SPANISH_PERU',                   0x0a),
 (u'SUBLANG_SPANISH_ARGENTINA',              0x0b),
 (u'SUBLANG_SPANISH_ECUADOR',                0x0c),
 (u'SUBLANG_SPANISH_CHILE',                  0x0d),
 (u'SUBLANG_SPANISH_URUGUAY',                0x0e),
 (u'SUBLANG_SPANISH_PARAGUAY',               0x0f),
 (u'SUBLANG_SPANISH_BOLIVIA',                0x10),
 (u'SUBLANG_SPANISH_EL_SALVADOR',            0x11),
 (u'SUBLANG_SPANISH_HONDURAS',               0x12),
 (u'SUBLANG_SPANISH_NICARAGUA',              0x13),
 (u'SUBLANG_SPANISH_PUERTO_RICO',            0x14),
 (u'SUBLANG_SWEDISH',                        0x01),
 (u'SUBLANG_SWEDISH_FINLAND',                0x02),
 (u'SUBLANG_URDU_PAKISTAN',                  0x01),
 (u'SUBLANG_URDU_INDIA',                     0x02),
 (u'SUBLANG_UZBEK_LATIN',                    0x01),
 (u'SUBLANG_UZBEK_CYRILLIC',                 0x02),
 (u'SUBLANG_DUTCH_SURINAM',                  0x03),
 (u'SUBLANG_ROMANIAN',                       0x01),
 (u'SUBLANG_ROMANIAN_MOLDAVIA',              0x02),
 (u'SUBLANG_RUSSIAN',                        0x01),
 (u'SUBLANG_RUSSIAN_MOLDAVIA',               0x02),
 (u'SUBLANG_CROATIAN',                       0x01),
 (u'SUBLANG_LITHUANIAN_CLASSIC',             0x02),
 (u'SUBLANG_GAELIC',                         0x01),
 (u'SUBLANG_GAELIC_SCOTTISH',                0x02),
 (u'SUBLANG_GAELIC_MANX',                    0x03) ]

SUBLANG = dict(sublang+[(e[1], e[0]) for e in sublang])

#Identifier   Language: http://msdn.microsoft.com/en-us/library/ms776264(VS.85).aspx
# define in: http://msdn.microsoft.com/en-us/library/ms776260(VS.85).aspx
LANGUAGE = {
#Locale identifier     Locale                                       Locale name    Script tag       ANSI code page          comment
    0x0000: (u'Language Neutral',                                   None,           None,               None,               None,),
    0x0C00: (u'Default custom locale',                              None,           None,               None,               u'Windows Vista and later',),
    0x1400: (u'Default custom Multilingual User Interface (MUI) locale',None,       None,               None,               u'Windows Vista and later',),
    0x1000: (u'Custom locale',                                      None,           None,               None,               u'Windows Vista and later',),
    0x007f: (u'Invariant locale',                                   None,           None,               None,               None,),
    0x0436: (u'Afrikaans (South Africa)',                           u'af-ZA',       u'Latn',            1252,               None,),
    0x041c: (u'Albanian (Albania)',                                 u'sq-AL',       u'Latn',            1252,               None,),
    0x0484: (u'Alsatian (France)',                                  u'gsw-FR',      None,               None,               None,),            
    0x045e: (u'Amharic (Ethiopia)',                                 u'am-ET',       None,               u'Unicode only',    u'Windows Vista and later'), 
    0x1401: (u'Arabic (Algeria)',                                   u'ar-DZ',       u'Arab',            1256,               None,),
    0x3c01: (u'Arabic (Bahrain)',                                   u'ar-BH',       u'Arab',            1256,               None,),
    0x0c01: (u'Arabic (Egypt)',                                     u'ar-EG',       u'Arab',            1256,               None,),
    0x0801: (u'Arabic (Iraq)',                                      u'ar-IQ',       u'Arab',            1256,               None,),
    0x2c01: (u'Arabic (Jordan)',                                    u'ar-JO',       u'Arab',            1256,               None,),
    0x3401: (u'Arabic (Kuwait)',                                    u'ar-KW',       u'Arab',            1256,               None,),
    0x3001: (u'Arabic (Lebanon)',                                   u'ar-LB',       u'Arab',            1256,               None,),
    0x1001: (u'Arabic (Libya)',                                     u'ar-LY',       u'Arab',            1256,               None,),
    0x1801: (u'Arabic (Morocco)',                                   u'ar-MA',       u'Arab',            1256,               None,),
    0x2001: (u'Arabic (Oman)',                                      u'ar-OM',       u'Arab',            1256,               None,),
    0x4001: (u'Arabic (Qatar)',                                     u'ar-QA',       u'Arab',            1256,               None,),
    0x0401: (u'Arabic (Saudi Arabia)',                              u'ar-SA',       u'Arab',            1256,               None,),
    0x2801: (u'Arabic (Syria)',                                     u'ar-SY',       u'Arab',            1256,               None,),
    0x1c01: (u'Arabic (Tunisia)',                                   u'ar-TN',       u'Arab',            1256,               None,),
    0x3801: (u'Arabic (U.A.E.)',                                    u'ar-AE',       u'Arab',            1256,               None,),
    0x2401: (u'Arabic (Yemen)',                                     u'ar-YE',       u'Arab',            1256,               None,),
    0x042b: (u'Armenian (Armenia)',                                 u'hy-AM',       u'Armn',            u'Unicode only',    u'Windows 2000 and later',),
    0x044d: (u'Assamese (India)',                                   u'as-IN',       None,               u'Unicode only',    u'Windows Vista and later',),
    0x082c: (u'Azeri (Azerbaijan, Cyrillic)',                       u'az-Cyrl-AZ',  u'Cyrl',            1251,               None,),
    0x042c: (u'Azeri (Azerbaijan, Latin)',                          u'az-Latn-AZ',  u'Latn',            1254,               None,),
    0x046d: (u'Bashkir (Russia)',                                   u'ba-RU',       None,               None,               u'Windows Vista and later',), 
    0x042d: (u'Basque (Basque)',                                    u'eu-ES',       u'Latn',            1252,               None,),
    0x0423: (u'Belarusian (Belarus)',                               u'be-BY',       u'Cyrl',            1251,               None,),
    0x0445: (u'Bengali (India)',                                    u'bn-IN',       u'Beng',            u'Unicode only',    u'Windows XP SP2 and later',),
    0x201a: (u'Bosnian (Bosnia and Herzegovina, Cyrillic)',         u'bs-Cyrl-BA',  u'Cyrl',            1251,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x141a: (u'Bosnian (Bosnia and Herzegovina, Latin)',            u'bs-Latn-BA',  u'Latn',            1250,               u'Windows XP SP2 and later',), 
    0x047e: (u'Breton (France)',                                    u'br-FR',       u'Latn',            1252,               None,),
    0x0402: (u'Bulgarian (Bulgaria)',                               u'bg-BG',       u'Cyrl',            1251,               None,),
    0x0455: (u'Burmese',                                            None,           None,               None,               u'Not supported',),
    0x0403: (u'Catalan (Catalan)',                                  u'ca-ES',       u'Latn',            1252,               None,),
    0x0c04: (u'Chinese (Hong Kong SAR, PRC)',                       u'zh-HK',       u'Hant',            950,                None,),
    0x1404: (u'Chinese (Macao SAR)',                                u'zh-MO',       u'Hant',            950,                u'Windows 98/Me, Windows XP and later',),
    0x0804: (u'Chinese (PRC)',                                      u'zh-CN',       u'Hans',            936,                None,),
    0x1004: (u'Chinese (Singapore)',                                u'zh-SG',       u'Hans',            936,                None,),
    0x0404: (u'Chinese (Taiwan)',                                   u'zh-TW',       u'Hant',            950,                None,),
          #Windows Vista and later: Corsican (France)     co-FR            
    0x101a: (u'Croatian (Bosnia and Herzegovina, Latin)',           u'hr-BA',       u'Latn',            1250,               u'Windows XP SP2 and later',), 
    0x041a: (u'Croatian (Croatia)',                                 u'hr-HR',       u'Latn',            1250,               None,),
    0x0405: (u'Czech (Czech Republic)',                             u'cs-CZ',       u'Latn',            1250,               None,),
    0x0406: (u'Danish (Denmark)',                                   u'da-DK',       u'Latn',            1252,               None,),
    0x048c: (u'Dari (Afghanistan)',                                 u'prs-AF',      u'Arab',            1256,               u'Windows XP and later',),
    0x0465: (u'Divehi (Maldives)',                                  u'dv-MV',       u'Thaa',            u'Unicode only',    u'Windows XP and later',),
    0x0813: (u'Dutch (Belgium)',                                    u'nl-BE',       u'Latn',            1252,               None,),
    0x0413: (u'Dutch (Netherlands)',                                u'nl-NL',       u'Latn',            1252,               None,),
    0x0c09: (u'English (Australia)',                                u'en-AU',       u'Latn',            1252,               None,),
    0x2809: (u'English (Belize)',                                   u'en-BZ',       u'Latn',            1252,               None,),
    0x1009: (u'English (Canada)',                                   u'en-CA',       u'Latn',            1252,               None,),
    0x2409: (u'English (Caribbean)',                                u'en-029',      u'Latn',            1252,               None,),
    0x4009: (u'English (India)',                                    u'en-IN',       u'Latn',            1252,               u'Windows Vista and later',),
    0x1809: (u'English (Ireland)',                                  u'en-IE',       u'Latn',            1252,               None,),
    0x2009: (u'English (Jamaica)',                                  u'en-JM',       u'Latn',            1252,               None,),
    0x4409: (u'English (Malaysia)',                                 u'en-MY',       u'Latn',            1252,               u'Windows Vista and later',),
    0x1409: (u'English (New Zealand)',                              u'en-NZ',       u'Latn',            1252,               None,),
    0x3409: (u'English (Philippines)',                              u'en-PH',       u'Latn',            1252,               u'Windows 98/Me, Windows 2000 and later',),
    0x4809: (u'English (Singapore)',                                u'en-SG',       u'Latn',            1252,               u'Windows Vista and later',),
    0x1c09: (u'English (South Africa)',                             u'en-ZA',       u'Latn',            1252,               None,),
    0x2c09: (u'English (Trinidad and Tobago)',                      u'en-TT',       u'Latn',            1252,               None,),
    0x0809: (u'English (United Kingdom)',                           u'en-GB',       u'Latn',            1252,               None,),
    0x0409: (u'English (United States)',                            u'en-US',       u'Latn',            1252,               None,),
    0x3009: (u'English (Zimbabwe)',                                 u'en-ZW',       u'Latn',            1252,               u'Windows 98/Me, Windows 2000 and later',),
    0x0425: (u'Estonian (Estonia)',                                 u'et-EE',       u'Latn',            1257,               None,),
    0x0438: (u'Faroese (Faroe Islands)',                            u'fo-FO',       u'Latn',            1252,               None,),
    0x0464: (u'Filipino (Philippines)',                             u'fil-PH',      u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',),
    0x040b: (u'Finnish (Finland)',                                  u'fi-FI',       u'Latn',            1252,               None,),
    0x080c: (u'French (Belgium)',                                   u'fr-BE',       u'Latn',            1252,               None,),
    0x0c0c: (u'French (Canada)',                                    u'fr-CA',       u'Latn',            1252,               None,),
    0x040c: (u'French (France)',                                    u'fr-FR',       u'Latn',            1252,               None,),
    0x140c: (u'French (Luxembourg)',                                u'fr-LU',       u'Latn',            1252,               None,),
    0x180c: (u'French (Monaco)',                                    u'fr-MC',       u'Latn',            1252,               None,),
    0x100c: (u'French (Switzerland)',                               u'fr-CH',       u'Latn',            1252,               None,),
    0x0462: (u'Frisian (Netherlands)',                              u'fy-NL',       u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x0456: (u'Galician (Spain)',                                   u'gl-ES',       u'Latn',            1252,               u'Windows XP and later',),
    0x0437: (u'Georgian (Georgia)',                                 u'ka-GE',       u'Geor',            u'Unicode only',    u'Windows 2000 and later',),
    0x0c07: (u'German (Austria)',                                   u'de-AT',       u'Latn',            1252,               None,),
    0x0407: (u'German (Germany)',                                   u'de-DE',       u'Latn',            1252,               None,),
    0x1407: (u'German (Liechtenstein)',                             u'de-LI',       u'Latn',            1252,               None,),
    0x1007: (u'German (Luxembourg)',                                u'de-LU',       u'Latn',            1252,               None,),
    0x0807: (u'German (Switzerland)',                               u'de-CH',       u'Latn',            1252,               None,),
    0x0408: (u'Greek (Greece)',                                     u'el-GR',       u'Grek',            1253,               None,),
    0x046f: (u'Greenlandic (Greenland)',                            u'kl-GL',       u'Latn',            1252,               u'Windows Vista and later',),
    0x0447: (u'Gujarati (India)',                                   u'gu-IN',       u'Gujr',            u'Unicode only',    u'Windows XP and later',),
    0x0468: (u'Hausa (Nigeria, Latin)',                             u'ha-Latn-NG',  u'Latn',            1252,               u'Windows Vista and later',),
    0x040d: (u'Hebrew (Israel)',                                    u'he-IL',       u'Hebr',            1255,               None,),
    0x0439: (u'Hindi (India)',                                      u'hi-IN',       u'Deva',            u'Unicode only',    u'Windows 2000 and later',),
    0x040e: (u'Hungarian (Hungary)',                                u'hu-HU',       u'Latn',            1250,               None,),
    0x040f: (u'Icelandic (Iceland)',                                u'is-IS',       u'Latn',            1252,               None,),
    0x0470: (u'Igbo (Nigeria)',                                     u'ig-NG',       None,               None,               None,),
    0x0421: (u'Indonesian (Indonesia)',                             u'id-ID',       u'Latn',            1252,               None,),
    0x085d: (u'Inuktitut (Canada, Latin)',                          u'iu-Latn-CA',  u'Latn',            1252,               u'Windows XP and later',),
    0x045d: (u'Inuktitut (Canada, Syllabics)',                      u'iu-Cans-CA',  u'Cans',            u'Unicode only',    u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x083c: (u'Irish (Ireland)',                                    u'ga-IE',       u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x0410: (u'Italian (Italy)',                                    u'it-IT',       u'Latn',            1252,               None,),
    0x0810: (u'Italian (Switzerland)',                              u'it-CH',       u'Latn',            1252,               None,),
    0x0411: (u'Japanese (Japan)',                                   u'ja-JP',       u'Hani;Hira;Kana',  932,                None,),
    0x044b: (u'Kannada (India)',                                    u'kn-IN',       u'Knda',            u'Unicode only',    u'Windows XP and later',),
    0x043f: (u'Kazakh (Kazakhstan)',                                u'kk-KZ',       u'Cyrl',            1251,               u'Windows 2000 and later',),
    0x0453: (u'Khmer (Cambodia)',                                   u'kh-KH',       u'Khmr',            u'Unicode only',    u'Windows Vista and later',),
    0x0486: (u'K\'iche (Guatemala)',                                u'qut-GT',      u'Latn',            1252,               u'Windows Vista and later',),
    0x0487: (u'Kinyarwanda (Rwanda)',                               u'rw-RW',       u'Latn',            1252,               u'Windows Vista and later',),
    0x0457: (u'Konkani (India)',                                    u'kok-IN',      u'Deva',            u'Unicode only',    u'Windows 2000 and later',),
    0x0812: (u'Korean (Johab)',                                     None,           None,               None,               u'Windows 95, Windows NT 4.0 only',),
    0x0412: (u'Korean (Korea)',                                     u'ko-KR',       u'Hang;Hani',       949,                None,),
    0x0440: (u'Kyrgyz (Kyrgyzstan)',                                u'ky-KG',       u'Cyrl',            1251,               u'Windows XP and later',),
    0x0454: (u'Lao (Lao PDR)',                                      u'lo-LA',       u'Laoo',            u'Unicode only',    u'Windows Vista and later',),
    0x0426: (u'Latvian (Latvia)',                                   u'lv-LV',       u'Latn',            1257,               None,),
    0x0427: (u'Lithuanian (Lithuania)',                             u'lt-LT',       u'Latn',            1257,               None,),
    0x082e: (u'Lower Sorbian (Germany)',                            u'dsb-DE',      u'Latn',            1252,               u'Windows Vista and later',),
    0x046e: (u'Luxembourgish (Luxembourg)',                         u'lb-LU',       u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x042f: (u'Macedonian (Macedonia, FYROM)',                      u'mk-MK',       u'Cyrl',            1251,               u'Windows 2000 and later',),
    0x083e: (u'Malay (Brunei Darussalam)',                          u'ms-BN',       u'Latn',            1252,               u'Windows 2000 and later',),
    0x043e: (u'Malay (Malaysia)',                                   u'ms-MY',       u'Latn',            1252,               u'Windows 2000 and later',),
    0x044c: (u'Malayalam (India)',                                  u'ml-IN',       u'Mlym',            u'Unicode only',    u'Windows XP SP2 and later',),
    0x043a: (u'Maltese (Malta)',                                    u'mt-MT',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0481: (u'Maori (New Zealand)',                                u'mi-NZ',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x047a: (u'Mapudungun (Chile)',                                 u'arn-CL',      u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',),
    0x044e: (u'Marathi (India)',                                    u'mr-IN',       u'Deva',            u'Unicode only',    u'Windows 2000 and later',),
    0x047c: (u'Mohawk (Canada)',                                    u'moh-CA',      u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',),
    0x0450: (u'Mongolian (Mongolia)',                               u'mn-Cyrl-MN',  u'Cyrl',            1251,               u'Windows XP and later',),
    0x0850: (u'Mongolian (PRC)',                                    u'mn-Mong-CN',  u'Mong',            u'Unicode only',    u'Windows Vista and later',),
        #Nepali (India)     ne-IN     __     Unicode only
    0x0461: (u'Nepali (Nepal)',                                     u'ne-NP',       u'Deva',            u'Unicode only',    u'Windows XP SP2 and later (downloadable); Windows Vista and later',),
    0x0414: (u'Norwegian (Bokmål, Norway)',                         u'nb-NO',       u'Latn',            1252,               None,),
    0x0814: (u'Norwegian (Nynorsk, Norway)',                        u'nn-NO',       u'Latn',            1252,               None,),
    0x0482: (u'Occitan (France)',                                   u'oc-FR',       u'Latn',            1252,               None,),
    0x0448: (u'Oriya (India)',                                      u'or-IN',       u'Orya',            u'Unicode only',    None,),
    0x0463: (u'Pashto (Afghanistan)',                               u'ps-AF',       None,               None,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',), 
    0x0429: (u'Persian (Iran)',                                     u'fa-IR',       u'Arab',            1256,               None,),
    0x0415: (u'Polish (Poland)',                                    u'pl-PL',       u'Latn',            1250,               None,),
    0x0416: (u'Portuguese (Brazil)',                                u'pt-BR',       u'Latn',            1252,               None,),
    0x0816: (u'Portuguese (Portugal)',                              u'pt-PT',       u'Latn',            1252,               None,),
    0x0446: (u'Punjabi (India)',                                    u'pa-IN',       u'Guru',            u'Unicode only',    u'Windows XP and later',), 
    0x046b: (u'Quechua (Bolivia)',                                  u'quz-BO',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x086b: (u'Quechua (Ecuador)',                                  u'quz-EC',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0c6b: (u'Quechua (Peru)',                                     u'quz-PE',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0418: (u'Romanian (Romania)',                                 u'ro-RO',       u'Latn',            1250,               None,),
    0x0417: (u'Romansh (Switzerland)',                              u'rm-CH',       u'Latn',            1252,               u'Windows XP SP2 and later (downloadable); Windows Vista and later',),
    0x0419: (u'Russian (Russia)',                                   u'ru-RU',       u'Cyrl',            1251,               None,),
    0x243b: (u'Sami (Inari, Finland)',                              u'smn-FI',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x103b: (u'Sami (Lule, Norway)',                                u'smj-NO',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x143b: (u'Sami (Lule, Sweden)',                                u'smj-SE',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0c3b: (u'Sami (Northern, Finland)',                           u'se-FI',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x043b: (u'Sami (Northern, Norway)',                            u'se-NO',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x083b: (u'Sami (Northern, Sweden)',                            u'se-SE',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x203b: (u'Sami (Skolt, Finland)',                              u'sms-FI',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x183b: (u'Sami (Southern, Norway)',                            u'sma-NO',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x1c3b: (u'Sami (Southern, Sweden)',                            u'sma-SE',      u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x044f: (u'Sanskrit (India)',                                   u'sa-IN',       u'Deva',            u'Unicode only',    u'Windows 2000 and later',),
    0x1c1a: (u'Serbian (Bosnia and Herzegovina, Cyrillic)',         u'sr-Cyrl-BA',  u'Cyrl',            1251,               u'Windows XP SP2 and later',),
    0x181a: (u'Serbian (Bosnia and Herzegovina, Latin)',            u'sr-Latn-BA',  u'Latn',            1250,               u'Windows XP SP2 and later',),
    0x0c1a: (u'Serbian (Serbia, Cyrillic)',                         u'sr-Cyrl-CS',  u'Cyrl',            1251,               None,),
    0x081a: (u'Serbian (Serbia, Latin)',                            u'sr-Latn-CS',  u'Latn',            1250,               None,),
    0x046c: (u'Sesotho sa Leboa/Northern Sotho (South Africa)',     u'ns-ZA',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0432: (u'Setswana/Tswana (South Africa)',                     u'tn-ZA',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x045b: (u'Sinhala (Sri Lanka)',                                u'si-LK',       u'Sinh',            u'Unicode only',    u'Windows Vista and later',),
    0x041b: (u'Slovak (Slovakia)',                                  u'sk-SK',       u'Latn',            1250,               None,),
    0x0424: (u'Slovenian (Slovenia)',                               u'sl-SI',       u'Latn',            1250,               None,),
    0x2c0a: (u'Spanish (Argentina)',                                u'es-AR',       u'Latn',            1252,               None,),
    0x400a: (u'Spanish (Bolivia)',                                  u'es-BO',       u'Latn',            1252,               None,),
    0x340a: (u'Spanish (Chile)',                                    u'es-CL',       u'Latn',            1252,               None,),
    0x240a: (u'Spanish (Colombia)',                                 u'es-CO',       u'Latn',            1252,               None,),
    0x140a: (u'Spanish (Costa Rica)',                               u'es-CR',       u'Latn',            1252,               None,),
    0x1c0a: (u'Spanish (Dominican Republic)',                       u'es-DO',       u'Latn',            1252,               None,),
    0x300a: (u'Spanish (Ecuador)',                                  u'es-EC',       u'Latn',            1252,               None,),
    0x440a: (u'Spanish (El Salvador)',                              u'es-SV',       u'Latn',            1252,               None,),
    0x100a: (u'Spanish (Guatemala)',                                u'es-GT',       u'Latn',            1252,               None,),
    0x480a: (u'Spanish (Honduras)',                                 u'es-HN',       u'Latn',            1252,               None,),
    0x080a: (u'Spanish (Mexico)',                                   u'es-MX',       u'Latn',            1252,               None,),
    0x4c0a: (u'Spanish (Nicaragua)',                                u'es-NI',       u'Latn',            1252,               None,),
    0x180a: (u'Spanish (Panama)',                                   u'es-PA',       u'Latn',            1252,               None,),
    0x3c0a: (u'Spanish (Paraguay)',                                 u'es-PY',       u'Latn',            1252,               None,),
    0x280a: (u'Spanish (Peru)',                                     u'es-PE',       u'Latn',            1252,               None,),
    0x500a: (u'Spanish (Puerto Rico)',                              u'es-PR',       u'Latn',            1252,               None,),
    0x0c0a: (u'Spanish (Spain)',                                    u'es-ES',       u'Latn',            1252,               None,),
    0x040a: (u'Spanish (Spain, Traditional Sort)',                  u'es-ES_tradnl',u'Latn',            1252,               None,),
    0x540a: (u'Spanish (United States)',                            u'es-US',       None,               None,               u'Windows Vista and later',), 
    0x380a: (u'Spanish (Uruguay)',                                  u'es-UY',       u'Latn',            1252,               None,),
    0x200a: (u'Spanish (Venezuela)',                                u'es-VE',       u'Latn',            1252,               None,),
    0x0430: (u'Sutu',                                               None,           None,               None,               u'Not supported',),
    0x0441: (u'Swahili (Kenya)',                                    u'sw-KE',       u'Latn',            1252,               u'Windows 2000 and later',),
    0x081d: (u'Swedish (Finland)',                                  u'sv-FI',       u'Latn',            1252,               None,),
    0x041d: (u'Swedish (Sweden)',                                   u'sv-SE',       u'Latn',            1252,               None,),
    0x045a: (u'Syriac (Syria)',                                     u'syr-SY',      u'Syrc',            u'Unicode only',    u'Windows XP and later',),
    0x0428: (u'Tajik (Tajikistan)',                                 u'tg-Cyrl-TJ',  u'Cyrl',            1251,               u'Windows Vista and later',),
    0x085f: (u'Tamazight (Algeria, Latin)',                         u'tzm-Latn-DZ', u'Latn',            1252,               u'Windows Vista and later',),
    0x0449: (u'Tamil (India)',                                      u'ta-IN',       u'Taml',            u'Unicode only',    u'Windows 2000 and later',),
    0x0444: (u'Tatar (Russia)',                                     u'tt-RU',       u'Cyrl',            1251,               u'Windows XP and later',),
    0x044a: (u'Telugu (India)',                                     u'te-IN',       u'Telu',            u'Unicode only',    u'Windows XP and later',),
    0x041e: (u'Thai (Thailand)',                                    u'th-TH',       u'Thai',            874,                None,),
    0x0851: (u'Tibetan (Bhutan)',                                   u'bo-BT',       u'Tibt',            u'Unicode only',    u'Windows Vista and later',),
    0x0451: (u'Tibetan (PRC)',                                      u'bo-CN',       u'Tibt',            u'Unicode only',    u'Windows Vista and later',),
    0x041f: (u'Turkish (Turkey)',                                   u'tr-TR',       u'Latn',            1254,               None,),
    0x0442: (u'Turkmen (Turkmenistan)',                             u'tk-TM',       u'Cyrl',            1251,               u'Windows Vista and later',),
    0x0480: (u'Uighur (PRC)',                                       u'ug-CN',       u'Arab',            1256,               u'Windows Vista and later',),
    0x0422: (u'Ukrainian (Ukraine)',                                u'uk-UA',       u'Cyrl',            1251,               None,),
    0x042e: (u'Upper Sorbian (Germany)',                            u'wen-DE',      u'Latn',            1252,               u'Windows Vista and later',),
    0x0820: (u'Urdu (India)',                                       u'tr-IN',       None,               None,               None,),
    0x0420: (u'Urdu (Pakistan)',                                    u'ur-PK',       u'Arab',            1256,               u'Windows 98/Me, Windows 2000 and later',),
    0x0843: (u'Uzbek (Uzbekistan, Cyrillic)',                       u'uz-Cyrl-UZ',  u'Cyrl',            1251,               u'Windows 2000 and later',),
    0x0443: (u'Uzbek (Uzbekistan, Latin)',                          u'uz-Latn-UZ',  u'Latn',            1254,               u'Windows 2000 and later',),
    0x042a: (u'Vietnamese (Vietnam)',                               u'vi-VN',       u'Latn',            1258,               u'Windows 98/Me, Windows NT 4.0 and later',),
    0x0452: (u'Welsh (United Kingdom)',                             u'cy-GB',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0488: (u'Wolof (Senegal)',                                    u'wo-SN',       u'Latn',            1252,               u'Windows Vista and later',), 
    0x0434: (u'Xhosa/isiXhosa (South Africa)',                      u'xh-ZA',       u'Latn',            1252,               u'Windows XP SP2 and later',),
    0x0485: (u'Yakut (Russia)',                                     u'sah-RU',      u'Cyrl',            1251,               u'Windows Vista and later',),
    0x0478: (u'Yi (PRC)',                                           u'ii-CN',       u'Yiii',            u'Unicode only',    u'Windows Vista and later',),
    0x046a: (u'Yoruba (Nigeria)',                                   u'yo-NG',       None,               None,               u'Windows Vista and later',),
    0x0435: (u'Zulu/isiZulu (South Africa)',                        u'zu-ZA',       u'Latn',            1252,               u'Windows XP SP2 and later',),
}

#Code Page Identifiers, also see: http://msdn.microsoft.com/en-us/library/ms776446(VS.85).aspx
CODEPAGE = {
# Identifier     .NET Name     Additional information
      037: (u'IBM037',                           u'IBM EBCDIC US-Canada',),
      437: (u'IBM437',                           u'OEM United States',),
      500: (u'IBM500',                           u'IBM EBCDIC International',),
      708: (u'ASMO-708',                         u'Arabic (ASMO 708)',),
      709: (None,                                u'Arabic (ASMO-449+, BCON V4)',),
      710: (None,                                u'Arabic - Transparent Arabic',),
      720: (u'DOS-720',                          u'Arabic (Transparent ASMO); Arabic (DOS)',),
      737: (u'ibm737',                           u'OEM Greek (formerly 437G); Greek (DOS)',),
      775: (u'ibm775',                           u'OEM Baltic; Baltic (DOS)',),
      850: (u'ibm850',                           u'OEM Multilingual Latin 1; Western European (DOS)',),
      852: (u'ibm852',                           u'OEM Latin 2; Central European (DOS)',),
      855: (u'IBM855',                           u'OEM Cyrillic (primarily Russian)',),
      857: (u'ibm857',                           u'OEM Turkish; Turkish (DOS)',),
      858: (u'IBM00858',                         u'OEM Multilingual Latin 1 + Euro symbol',),
      860: (u'IBM860',                           u'OEM Portuguese; Portuguese (DOS)',),
      861: (u'ibm861',                           u'OEM Icelandic; Icelandic (DOS)',),
      862: (u'DOS-862',                          u'OEM Hebrew; Hebrew (DOS)',),
      863: (u'IBM863',                           u'OEM French Canadian; French Canadian (DOS)',),
      864: (u'IBM864',                           u'OEM Arabic; Arabic (864)',),
      865: (u'IBM865',                           u'OEM Nordic; Nordic (DOS)',),
      866: (u'cp866',                            u'OEM Russian; Cyrillic (DOS)',),
      869: (u'ibm869',                           u'OEM Modern Greek; Greek, Modern (DOS)',),
      870: (u'IBM870',                           u'IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2',),
      874: (u'windows-874',                      u'ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows)',),
      875: (u'cp875',                            u'IBM EBCDIC Greek Modern',),
      932: (u'shift_jis',                        u'ANSI/OEM Japanese; Japanese (Shift-JIS)',),
      936: (u'gb2312',                           u'ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)',),
      949: (u'ks_c_5601-1987',                   u'ANSI/OEM Korean (Unified Hangul Code)',),
      950: (u'big5',                             u'ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)',),
     1026: (u'IBM1026',                          u'IBM EBCDIC Turkish (Latin 5)',),
     1047: (u'IBM01047',                         u'IBM EBCDIC Latin 1/Open System',),
     1140: (u'IBM01140',                         u'IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)',),
     1141: (u'IBM01141',                         u'IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)',),
     1142: (u'IBM01142',                         u'IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)',),
     1143: (u'IBM01143',                         u'IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)',),
     1144: (u'IBM01144',                         u'IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)',),
     1145: (u'IBM01145',                         u'IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)',),
     1146: (u'IBM01146',                         u'IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)',),
     1147: (u'IBM01147',                         u'IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)',),
     1148: (u'IBM01148',                         u'IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)',),
     1149: (u'IBM01149',                         u'IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)',),
     1200: (u'utf-16',                           u'Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications',),
     1201: (u'unicodeFFFE',                      u'Unicode UTF-16, big endian byte order; available only to managed applications',),
     1250: (u'windows-1250',                     u'ANSI Central European; Central European (Windows)',),
     1251: (u'windows-1251',                     u'ANSI Cyrillic; Cyrillic (Windows)',),
     1252: (u'windows-1252',                     u'ANSI Latin 1; Western European (Windows)',),
     1253: (u'windows-1253',                     u'ANSI Greek; Greek (Windows)',),
     1254: (u'windows-1254',                     u'ANSI Turkish; Turkish (Windows)',),
     1255: (u'windows-1255',                     u'ANSI Hebrew; Hebrew (Windows)',),
     1256: (u'windows-1256',                     u'ANSI Arabic; Arabic (Windows)',),
     1257: (u'windows-1257',                     u'ANSI Baltic; Baltic (Windows)',),
     1258: (u'windows-1258',                     u'ANSI/OEM Vietnamese; Vietnamese (Windows)',),
     1361: (u'Johab',                            u'Korean (Johab)',),
    10000: (u'macintosh',                        u'MAC Roman; Western European (Mac)',),
    10001: (u'x-mac-japanese',                   u'Japanese (Mac)',),
    10002: (u'x-mac-chinesetrad',                u'MAC Traditional Chinese (Big5); Chinese Traditional (Mac)',),
    10003: (u'x-mac-korean',                     u'Korean (Mac)',),
    10004: (u'x-mac-arabic',                     u'Arabic (Mac)',),
    10005: (u'x-mac-hebrew',                     u'Hebrew (Mac)',),
    10006: (u'x-mac-greek',                      u'Greek (Mac)',),
    10007: (u'x-mac-cyrillic',                   u'Cyrillic (Mac)',),
    10008: (u'x-mac-chinesesimp',                u'MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac)',),
    10010: (u'x-mac-romanian',                   u'Romanian (Mac)',),
    10017: (u'x-mac-ukrainian',                  u'Ukrainian (Mac)',),
    10021: (u'x-mac-thai',                       u'Thai (Mac)',),
    10029: (u'x-mac-ce',                         u'MAC Latin 2; Central European (Mac)',),
    10079: (u'x-mac-icelandic',                  u'Icelandic (Mac)',),
    10081: (u'x-mac-turkish',                    u'Turkish (Mac)',),
    10082: (u'x-mac-croatian',                   u'Croatian (Mac)',),
    12000: (u'utf-32',                           u'Unicode UTF-32, little endian byte order; available only to managed applications',),
    12001: (u'utf-32BE',                         u'Unicode UTF-32, big endian byte order; available only to managed applications',),
    20000: (u'x-Chinese_CNS',                    u'CNS Taiwan; Chinese Traditional (CNS)',),
    20001: (u'x-cp20001',                        u'TCA Taiwan',),
    20002: (u'x_Chinese-Eten',                   u'Eten Taiwan; Chinese Traditional (Eten)',),
    20003: (u'x-cp20003',                        u'IBM5550 Taiwan',),
    20004: (u'x-cp20004',                        u'TeleText Taiwan',),
    20005: (u'x-cp20005',                        u'Wang Taiwan',),
    20105: (u'x-IA5',                            u'IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5)',),
    20106: (u'x-IA5-German',                     u'IA5 German (7-bit)',),
    20107: (u'x-IA5-Swedish',                    u'IA5 Swedish (7-bit)',),
    20108: (u'x-IA5-Norwegian',                  u'IA5 Norwegian (7-bit)',),
    20127: (u'us-ascii',                         u'US-ASCII (7-bit)',),
    20261: (u'x-cp20261',                        u'T.61',),
    20269: (u'x-cp20269',                        u'ISO 6937 Non-Spacing Accent',),
    20273: (u'IBM273',                           u'IBM EBCDIC Germany',),
    20277: (u'IBM277',                           u'IBM EBCDIC Denmark-Norway',),
    20278: (u'IBM278',                           u'IBM EBCDIC Finland-Sweden',),
    20280: (u'IBM280',                           u'IBM EBCDIC Italy',),
    20284: (u'IBM284',                           u'IBM EBCDIC Latin America-Spain',),
    20285: (u'IBM285',                           u'IBM EBCDIC United Kingdom',),
    20290: (u'IBM290',                           u'IBM EBCDIC Japanese Katakana Extended',),
    20297: (u'IBM297',                           u'IBM EBCDIC France',),
    20420: (u'IBM420',                           u'IBM EBCDIC Arabic',),
    20423: (u'IBM423',                           u'IBM EBCDIC Greek',),
    20424: (u'IBM424',                           u'IBM EBCDIC Hebrew',),
    20833: (u'x-EBCDIC-KoreanExtended',          u'IBM EBCDIC Korean Extended',),
    20838: (u'IBM-Thai',                         u'IBM EBCDIC Thai',),
    20866: (u'koi8-r',                           u'Russian (KOI8-R); Cyrillic (KOI8-R)',),
    20871: (u'IBM871',                           u'IBM EBCDIC Icelandic',),
    20880: (u'IBM880',                           u'IBM EBCDIC Cyrillic Russian',),
    20905: (u'IBM905',                           u'IBM EBCDIC Turkish',),
    20924: (u'IBM00924',                         u'IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)',),
    20932: (u'EUC-JP',                           u'Japanese (JIS 0208-1990 and 0121-1990)',),
    20936: (u'x-cp20936',                        u'Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)',),
    20949: (u'x-cp20949',                        u'Korean Wansung',),
    21025: (u'cp1025',                           u'IBM EBCDIC Cyrillic Serbian-Bulgarian',),
    21027: (None,                                u'(deprecated)',),
    21866: (u'koi8-u',                           u'Ukrainian (KOI8-U); Cyrillic (KOI8-U)',),
    28591: (u'iso-8859-1',                       u'ISO 8859-1 Latin 1; Western European (ISO)',),
    28592: (u'iso-8859-2',                       u'ISO 8859-2 Central European; Central European (ISO)',),
    28593: (u'iso-8859-3',                       u'ISO 8859-3 Latin 3',),
    28594: (u'iso-8859-4',                       u'ISO 8859-4 Baltic',),
    28595: (u'iso-8859-5',                       u'ISO 8859-5 Cyrillic',),
    28596: (u'iso-8859-6',                       u'ISO 8859-6 Arabic',),
    28597: (u'iso-8859-7',                       u'ISO 8859-7 Greek',),
    28598: (u'iso-8859-8',                       u'ISO 8859-8 Hebrew; Hebrew (ISO-Visual)',),
    28599: (u'iso-8859-9',                       u'ISO 8859-9 Turkish',),
    28603: (u'iso-8859-13',                      u'ISO 8859-13 Estonian',),
    28605: (u'iso-8859-15',                      u'ISO 8859-15 Latin 9',),
    29001: (u'x-Europa',                         u'Europa 3',),
    38598: (u'iso-8859-8-i',                     u'ISO 8859-8 Hebrew; Hebrew (ISO-Logical)',),
    50220: (u'iso-2022-jp',                      u'ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)',),
    50221: (u'csISO2022JP',                      u'ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)',),
    50222: (u'iso-2022-jp',                      u'ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)',),
    50225: (u'iso-2022-kr',                      u'ISO 2022 Korean',),
    50227: (u'x-cp50227',                        u'ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022)',),
    50229: (None,                                u'ISO 2022 Traditional Chinese',),
    50930: (None,                                u'EBCDIC Japanese (Katakana) Extended',),
    50931: (None,                                u'EBCDIC US-Canada and Japanese',),
    50933: (None,                                u'EBCDIC Korean Extended and Korean',),
    50935: (None,                                u'EBCDIC Simplified Chinese Extended and Simplified Chinese',),
    50936: (None,                                u'EBCDIC Simplified Chinese',),
    50937: (None,                                u'EBCDIC US-Canada and Traditional Chinese',),
    50939: (None,                                u'EBCDIC Japanese (Latin) Extended and Japanese',),
    51932: (u'euc-jp',                           u'EUC Japanese',),
    51936: (u'EUC-CN',                           u'EUC Simplified Chinese; Chinese Simplified (EUC)',),
    51949: (u'euc-kr',                           u'EUC Korean',),
    51950: (None,                                u'EUC Traditional Chinese',),
    52936: (u'hz-gb-2312',                       u'HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)',),
    54936: (u'GB18030',                          u'Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)',),
    57002: (u'x-iscii-de',                       u'ISCII Devanagari',),
    57003: (u'x-iscii-be',                       u'ISCII Bengali',),
    57004: (u'x-iscii-ta',                       u'ISCII Tamil',),
    57005: (u'x-iscii-te',                       u'ISCII Telugu',),
    57006: (u'x-iscii-as',                       u'ISCII Assamese',),
    57007: (u'x-iscii-or',                       u'ISCII Oriya',),
    57008: (u'x-iscii-ka',                       u'ISCII Kannada',),
    57009: (u'x-iscii-ma',                       u'ISCII Malayalam',),
    57010: (u'x-iscii-gu',                       u'ISCII Gujarati',),
    57011: (u'x-iscii-pa',                       u'ISCII Punjabi',),
    65000: (u'utf-7',                            u'Unicode (UTF-7)',),
    65001: (u'utf-8',                            u'Unicode (UTF-8)',),
}
# the certificate version number
certificate_revision = [
    (u'WIN_CERT_REVISION_1_0', 0x0100),
    (u'WIN_CERT_REVISION_2_0', 0x0200), #the current version
]
REVISION = dict(certificate_revision+[(e[1], e[0]) for e in certificate_revision])

# the type of content
certificate_type = [
    (u'WIN_CERT_TYPE_X509', 0x0001), # an X.509 Certificate Not Supported
    (u'WIN_CERT_TYPE_PKCS_SIGNED_DATA', 0x0002), # a PKCS#7 SignedData structure
    (u'WIN_CERT_TYPE_RESERVED_1', 0x0003), # Reserved
    (u'WIN_CERT_TYPE_TS_STACK_SIGNED', 0x0004), #Terminal Server Protocol Stack Certificate signing Not Supported
]
CERTIFICATE_TYPE = dict(certificate_type+[(e[1], e[0]) for e in certificate_type])

class NotPEError(Exception):
    """Not a PE"""

class PEFormatError(Exception):
    """Generic PE format error exception."""
    def __init__(self, value):
        self.value = value

    def __str__(self):
        return repr(self.value)
    
class UnicodeStringWrapperPostProcessor:
    """This class attemps to help the process of identifying strings
    that might be plain Unicode or Pascal. A list of strings will be
    wrapped on it with the hope the overlappings will help make the 
    decission about their type."""
    
    def __init__(self, pe, rva_ptr):
        self.pe = pe
        self.rva_ptr = rva_ptr
        self.string = None

    def get_rva(self):
        """Get the RVA of the string."""
        return self.rva_ptr

    def __unicode__(self):
        """Return the escaped UNICODE representation of the string."""
        if self.string is None:
            return u''
        return self.string
    
#    def __str__(self):
#        """Return the escaped ASCII representation of the string."""
#        def convert_char(char):
#            if char in string.printable:
#                return char
#            else:
#                return r'\x%02x' % ord(char)
#
#        if self.string:
#            return ''.join([convert_char(c) for c in self.string])
#        return ''

    def invalidate(self):
        """Make this instance None, to express it's no known string type."""
        self = None

    def render_pascal_16(self):
        self.string = self.pe.get_string_u_at_rva(self.rva_ptr+2, 
            max_length=self.__get_pascal_16_length())
        
    def ask_pascal_16(self, next_rva_ptr):
        """The next RVA is taken to be the one immediately following this one.
        
        Such RVA could indicate the natural end of the string and will be checked
        with the possible length contained in the first word.
        """
        length = self.__get_pascal_16_length()
        if length == (next_rva_ptr - (self.rva_ptr+2)) / 2:
            self.length = length
            return True
        return False
       
    def __get_pascal_16_length(self):
        return self.__get_word_value_at_rva(self.rva_ptr)
    
    def __get_word_value_at_rva(self, rva):
        try:
            data = self.pe.get_data(self.rva_ptr, 2)
        except PEFormatError, e:
            return False
        if len(data) < 2:
            return False
        return struct.unpack('<H', data)[0]
    
    def ask_unicode_16(self, next_rva_ptr):
        """The next RVA is taken to be the one immediately following this one.
        
        Such RVA could indicate the natural end of the string and will be checked
        to see if there's a Unicode NULL character there.
        """
        if self.__get_word_value_at_rva(next_rva_ptr-2) == 0:
            self.length = next_rva_ptr - self.rva_ptr
            return True
        return False
    
    def render_unicode_16(self):
        """"""
        self.string = self.pe.get_string_u_at_rva(self.rva_ptr)

class Dump(object):
    """Convenience class for dumping the PE information."""
    def __init__(self):
        self.text = u''
        
    def add_lines(self, txt, indent=0):
        """Adds a list of lines.
        
        The list can be indented with the optional argument 'indent'.
        """
        for line in txt:
            self.add_line(line, indent)
        
    def add_line(self, txt, indent=0):
        """Adds a line.
        
        The line can be indented with the optional argument 'indent'.
        """
        self.add(txt + os.linesep, indent)
    
    def add(self, txt, indent=0):
        """Adds some text, no newline will be appended.
        
        The text can be indented with the optional argument 'indent'.
        """
        self.text += u' ' * indent + txt
    
    def add_header(self, txt):
        """Adds a header element."""
        self.add_line(u'-' * 10 + txt + u'-' * 10 + os.linesep)
        
    def add_newline(self):
        """Adds a newline."""
        self.text += os.linesep
        
    def get_text(self):
        """Get the text in its current state."""
        return self.text

class Data(object):
    """Wrap File like object, let it see to be a str."""
    def __init__(self, fileobj, size, offset=0):
        self.__fileobj = fileobj
        self.__size = size
        self.__offset = offset
    
    def __len__(self):
        return int(self.__size)
    
    def __getslice__(self, ind1, ind2):
        if ind2 == sys.maxint:
            size = self.__size
        else:
            size = ind2 - ind1
        size = ind2 - ind1
        if size > self.__size:
            raise IndexError(u'index: %d out of range, ind1: %d, ind2: %d, maxsize: %d' % (ind2, ind1, ind2, self.__size))
        self.__fileobj.seek(self.__offset+ind1)
        return self.__fileobj.read(size)
    
    def __getitem__(self, ind):
        if ind > self.__size - 1:
            raise IndexError(u'"%d out of range"' % ind)
        self.__fileobj.seek(self.__offset+ind)
        item = self.__fileobj.read(1)
        return item

class Structure(object):
    """Prepare structure object to extract members from data.
    
    Format is a list containing definitions for the elements
    of the structure.
    """
    
    def __init__(self, format, file_offset=0, name=None):
        """"Format is forced little endian, for big endian non Intel platforms"""
        self._format = '<'
        self._keys = []
        self._format_length = 0
        self._set_format(format[1])
        self._all_zeroes = False
        self._unpacked_data_elms = None
        self._file_offset = file_offset
        if name:
            self.name = name
        else:
            self.name = format[0]
    
    @property
    def format(self):
        return self._format
    
    @property
    def keys(self):
        return self._keys
    
    @property
    def all_zeroes(self):
        """Returns true is the unpacked data is all zeroes."""
        return self._all_zeroes
    
    @property
    def size(self):
        return self._format_length
        
    def get_file_offset(self):
        return self._file_offset

    def set_file_offset(self, offset):
        self._file_offset = offset
    
    file_offset = property(get_file_offset, set_file_offset)
    
    def _set_format(self, format):
        for elm in format:
            if ',' in elm:
                elm_type, elm_name = elm.split(',', 1)
                self._format += elm_type
                elm_names = elm_name.split(',')
                names = []
                for elm_name in elm_names:
                    if elm_name in self._keys:
                        search_list = [x[:len(elm_name)] for x in self._keys]
                        occ_count = search_list.count(elm_name)
                        elm_name = elm_name + u'_' + unicode(occ_count)
                    names.append(unicode(elm_name))
                # Some PE header structures have unions on them, so a certain
                # value might have different names, so each key has a list of
                # all the possible members referring to the data.
                self._keys.append(names)
        self._format_length = struct.calcsize(self._format)

    def unpack(self, data):
        if len(data) < self._format_length:
            raise PEFormatError(u'Data length less than expected header length.')
        buffer = data[self._file_offset : self._file_offset+self._format_length]
        self._all_zeroes = buffer.count(chr(0)) == len(buffer)
        try:
            self._unpacked_data_elms = struct.unpack(self._format, buffer)
        except:
            raise PEFormatError(u'Data length (%d) less than expected header length (%d).' \
                                % (len(buffer), self._format_length))
        for i in xrange(len(self._unpacked_data_elms)):
            for key in self._keys[i]:
                value = self._unpacked_data_elms[i]
                if isinstance(value, str):
                    value = value.decode(u'ascii', u'ignore')
                    value = u''.join(filter(lambda uc:uc != u'\x00', value))
                setattr(self, key, value)

    def __pack__(self):
        new_values = []
        for i in xrange(len(self._unpacked_data_elms)):
            for key in self._keys[i]:
                new_val = getattr(self, key)
                old_val = self._unpacked_data_elms[i]
                # In the case of Unions, when the first changed value
                # is picked the loop is exited
                if new_val != old_val:
                    break
            new_values.append(new_val)
        return struct.pack(self._format, *new_values)
        
    def __str__(self):
        return os.linesep.join(self.dump())
    
    def __unicode__(self):
        return os.linesep.join(self.dump())

    def __repr__(self):
        return u'<Structure: %s>' % (u' '.join([u' '.join(s.split()) for s in self.dump()]))
        
    def dump(self, indentation=0):
        """Returns a string representation of the structure."""
        dump = []
        dump.append(u'[%s]' % self.name)
        # Refer to the __set_format__ method for an explanation
        # of the following construct.
        for keys in self._keys:
            for key in keys:
                val = getattr(self, key)
                if isinstance(val, int) or isinstance(val, long):
                    val_str = u'0x%-8X' % (val)
                    if key == 'TimeDateStamp' or key == 'dwTimeStamp':
                        try:
                            val_str += u' [%s UTC]' % time.asctime(time.gmtime(val))
                        except exceptions.ValueError, e:
                            val_str += u' [INVALID TIME]'
                else:
                    if isinstance(val, str):
                        val = val.decode('ascii', 'ignore')
#                        val = (''.join(filter(lambda c:c != '\0', val)))
#                    val_str = (''.join(filter(lambda c:c != '\0', val)))
                    val_str = u''.join(filter(lambda uc:uc != u'\x00', val))
                dump.append(u'%-30s %s' % (key + u':', val_str))
        return dump

class SectionStructure(Structure):
    """Convenience section handling class."""

    def get_data(self, start, length=None):
        """Get data chunk from a section.
        
        Allows to query data from the section by passing the
        addresses where the PE file would be loaded by default.
        It is then possible to retrieve code and data by its real
        addresses as it would be if loaded.
        """
        offset = start - self.VirtualAddress
        if length:
            end = offset+length
        else:
            end = len(self.data)
        return self.data[offset:end]

    def get_rva_from_offset(self, offset):
        return offset - self.PointerToRawData + self.VirtualAddress

    def get_offset_from_rva(self, rva):
        return (rva - self.VirtualAddress) + self.PointerToRawData

    def contains_offset(self, offset):
        """Check whether the section contains the file offset provided."""
        if not self.PointerToRawData:
           # bss and other sections containing only uninitialized data must have 0
           # and do not take space in the file
           return False
        return self.PointerToRawData <= offset < self.VirtualAddress + self.SizeOfRawData

    def contains_rva(self, rva):
        """Check whether the section contains the address provided."""
        # PECOFF documentation v8 says:
        # The total size of the section when loaded into memory.
        # If this value is greater than SizeOfRawData, the section is zero-padded.
        # This field is valid only for executable images and should be set to zero
        # for object files.
        if len(self.data) < self.SizeOfRawData:
            size = self.Misc_VirtualSize
        else:
            size = max(self.SizeOfRawData, self.Misc_VirtualSize)
        return self.VirtualAddress <= rva < self.VirtualAddress + size

    def contains(self, rva):
        #print "DEPRECATION WARNING: you should use contains_rva() instead of contains()"
        return self.contains_rva(rva)
    
    def set_data(self, data):
        """Set the data belonging to the section."""
        self.data = data
        
    def get_entropy(self):
        """Calculate and return the entropy for the section."""
        return self.entropy_H(self.data[:])
    
    def get_hash_sha1(self):
        """Get the SHA-1 hex-digest of the section's data."""
        if sha1 is not None:
            return None
            return sha1(self.data[:]).hexdigest()
        
    def get_hash_sha256(self):
        """Get the SHA-256 hex-digest of the section's data."""
        if sha256 is not None:
            return None
            return sha256(self.data[:]).hexdigest()

    def get_hash_sha512(self):
        """Get the SHA-512 hex-digest of the section's data."""
        if sha512 is not None:
            return None
            return sha512(self.data[:]).hexdigest()

    def get_hash_md5(self):
        """Get the MD5 hex-digest of the section's data."""
        if md5 is not None:
            return None
            return md5(self.data[:]).hexdigest()

    def entropy_H(self, data):
        """Calculate the entropy of a chunk of data."""
#        if len(data) == 0:
        return 0.0
#        occurences = array.array('L', [0]*256)
#        for x in data:
#            occurences[ord(x)] += 1
#        entropy = 0
#        for x in occurences:
#            if x:
#                p_x = float(x) / len(data)
#                entropy -= p_x * math.log(p_x, 2)
#        return entropy

class DataContainer(object):
    """Generic data container."""
    
    def __init__(self, **args):
        for key, value in args.items():
            setattr(self, key, value)

class ImportDescData(DataContainer):
    """Holds import descriptor information.
    
    dll:        name of the imported DLL
    imports:    list of imported symbols (ImportData instances)
    struct:     IMAGE_IMPORT_DESCRIPTOR sctruture
    """

class ImportData(DataContainer):
    """Holds imported symbol's information.
    
    ordinal:    Ordinal of the symbol
    name:       Name of the symbol
    bound:      If the symbol is bound, this contains
                the address.
    """
    
class ExportDirData(DataContainer):
    """Holds export directory information.
                    
    struct:     IMAGE_EXPORT_DIRECTORY structure
    symbols:    list of exported symbols (ExportData instances)
"""
    
class ExportData(DataContainer):
    """Holds exported symbols' information.
    
    ordinal:    ordinal of the symbol
    address:    address of the symbol
    name:       name of the symbol (None if the symbol is
                exported by ordinal only)
    forwarder:  if the symbol is forwarded it will
                contain the name of the target symbol,
                None otherwise.
    """
              

class ResourceDirData(DataContainer):
    """Holds resource directory information.
    
    struct:     IMAGE_RESOURCE_DIRECTORY structure
    entries:    list of entries (ResourceDirEntryData instances)
    """
    
class ResourceDirEntryData(DataContainer):
    """Holds resource directory entry data.
    
    struct:     IMAGE_RESOURCE_DIRECTORY_ENTRY structure
    name:       If the resource is identified by name this
                attribute will contain the name string. None
                otherwise. If identified by id, the id is
                availabe at 'struct.Id'
    id:         the id, also in struct.Id
    directory:  If this entry has a lower level directory
                this attribute will point to the
                ResourceDirData instance representing it.
    data:       If this entry has no futher lower directories
                and points to the actual resource data, this
                attribute will reference the corresponding
                ResourceDataEntryData instance.
    (Either of the 'directory' or 'data' attribute will exist,
    but not both.)
    """

class ResourceDataEntryData(DataContainer):
    """Holds resource data entry information.
    
    struct:     IMAGE_RESOURCE_DATA_ENTRY structure
    lang:       Primary language ID
    sublang:    Sublanguage ID
    """

class DebugData(DataContainer):
    """Holds debug information.
    
    struct:     IMAGE_DEBUG_DIRECTORY structure
    """

class BaseRelocationData(DataContainer):
    """Holds base relocation information.
    
    struct:     IMAGE_BASE_RELOCATION structure
    entries:    list of relocation data (RelocationData instances)
    """
    
class RelocationData(DataContainer):
    """Holds relocation information.
    
    type:       Type of relocation
                The type string is can be obtained by
                RELOCATION_TYPE[type]
    rva:        RVA of the relocation
    """

class TlsData(DataContainer):
    """Holds TLS information.
    
    struct:     IMAGE_TLS_DIRECTORY structure
    """

class BoundImportDescData(DataContainer):
    """Holds bound import descriptor data.
    
    This directory entry will provide with information on the
    DLLs this PE files has been bound to (if bound at all).
    The structure will contain the name and timestamp of the
    DLL at the time of binding so that the loader can know
    whether it differs from the one currently present in the
    system and must, therefore, re-bind the PE's imports.
    
    struct:     IMAGE_BOUND_IMPORT_DESCRIPTOR structure
    name:       DLL name
    entries:    list of entries (BoundImportRefData instances)
                the entries will exist if this DLL has forwarded
                symbols. If so, the destination DLL will have an
                entry in this list.
    """

class BoundImportRefData(DataContainer):
    """Holds bound import forwader reference data.
    
    Contains the same information as the bound descriptor but
    for forwarded DLLs, if any.
    
    struct:     IMAGE_BOUND_FORWARDER_REF structure
    name:       dll name
    """

class PE:
    """A Portable Executable representation.
    
    This class provides access to most of the information in a PE file.
    
    It expects to be supplied the name of the file to load or PE data
    to process and an optional argument 'fast_load' (False by default)
    which controls whether to load all the directories information,
    which can be quite time consuming.
    
    pe = pefile.PE(filepath='module.dll')
    try:
        pe.parse()
    finally:
        pe.close() # must be close when pe is no longer use.
    
    would load 'module.dll' and process it. If the data would be already
    available in a buffer the same could be achieved with:
    
    #pe = pefile.PE(data=module_dll_data) not support now
    
    The "fast_load" can be set to a default by setting its value in the
    module itself by means,for instance, of a "pefile.fast_load = True".
    That will make all the subsequent instances not to load the
    whole PE structure. The "full_load" method can be used to parse
    the missing data at a later stage.
    
    Basic headers information will be available in the attributes:
    
    DOS_HEADER
    NT_HEADERS
    FILE_HEADER
    OPTIONAL_HEADER
    
    All of them will contain among their attrbitues the members of the
    corresponding structures as defined in WINNT.H
    
    The raw data corresponding to the header (from the beginning of the
    file up to the start of the first section) will be avaiable in the
    instance's attribute 'header' as a string.
    
    The sections will be available as a list in the 'sections' attribute.
    Each entry will contain as attributes all the structure's members.
    
    Directory entries will be available as attributes (if they exist):
    (no other entries are processed at this point)
    
    DIRECTORY_ENTRY_IMPORT (list of ImportDescData instances)
    DIRECTORY_ENTRY_EXPORT (ExportDirData instance)
    DIRECTORY_ENTRY_RESOURCE (ResourceDirData instance)
    DIRECTORY_ENTRY_DEBUG (list of DebugData instances)
    DIRECTORY_ENTRY_BASERELOC (list of BaseRelocationData instances)
    DIRECTORY_ENTRY_TLS 
    DIRECTORY_ENTRY_BOUND_IMPORT (list of BoundImportData instances)
    
    The following dictionary attributes provide ways of mapping different
    constants. They will accept the numeric value and return the string
    representation and the opposite, feed in the string and get the
    numeric constant:
    
    DIRECTORY_ENTRY
    IMAGE_CHARACTERISTICS
    SECTION_CHARACTERISTICS
    DEBUG_TYPE
    SUBSYSTEM_TYPE
    MACHINE_TYPE
    RELOCATION_TYPE
    RESOURCE_TYPE
    LANG
    SUBLANG
    """

    # Format specifications for PE structures.
    IMAGE_DOS_HEADER_FORMAT = (u'IMAGE_DOS_HEADER',
        ('H,e_magic', 'H,e_cblp', 'H,e_cp',
        'H,e_crlc', 'H,e_cparhdr', 'H,e_minalloc',
        'H,e_maxalloc', 'H,e_ss', 'H,e_sp', 'H,e_csum',
        'H,e_ip', 'H,e_cs', 'H,e_lfarlc', 'H,e_ovno', '8s,e_res',
        'H,e_oemid', 'H,e_oeminfo', '20s,e_res2',
        'L,e_lfanew'))
        
    IMAGE_FILE_HEADER_FORMAT = (u'IMAGE_FILE_HEADER',
        ('H,Machine', 'H,NumberOfSections',
        'L,TimeDateStamp', 'L,PointerToSymbolTable',
        'L,NumberOfSymbols', 'H,SizeOfOptionalHeader',
        'H,Characteristics'))
        
    IMAGE_DATA_DIRECTORY_FORMAT = (u'IMAGE_DATA_DIRECTORY',
        ('L,VirtualAddress', 'L,Size'))

    IMAGE_OPTIONAL_HEADER_FORMAT = (u'IMAGE_OPTIONAL_HEADER',
        ('H,Magic', 'B,MajorLinkerVersion',
        'B,MinorLinkerVersion', 'L,SizeOfCode',
        'L,SizeOfInitializedData', 'L,SizeOfUninitializedData',
        'L,AddressOfEntryPoint', 'L,BaseOfCode', 'L,BaseOfData',
        'L,ImageBase', 'L,SectionAlignment', 'L,FileAlignment',
        'H,MajorOperatingSystemVersion', 'H,MinorOperatingSystemVersion',
        'H,MajorImageVersion', 'H,MinorImageVersion',
        'H,MajorSubsystemVersion', 'H,MinorSubsystemVersion',
        'L,Reserved1', 'L,SizeOfImage', 'L,SizeOfHeaders',
        'L,CheckSum', 'H,Subsystem', 'H,DllCharacteristics',
        'L,SizeOfStackReserve', 'L,SizeOfStackCommit',
        'L,SizeOfHeapReserve', 'L,SizeOfHeapCommit',
        'L,LoaderFlags', 'L,NumberOfRvaAndSizes' ))

    IMAGE_OPTIONAL_HEADER64_FORMAT = (u'IMAGE_OPTIONAL_HEADER64',
        ('H,Magic', 'B,MajorLinkerVersion',
        'B,MinorLinkerVersion', 'L,SizeOfCode',
        'L,SizeOfInitializedData', 'L,SizeOfUninitializedData',
        'L,AddressOfEntryPoint', 'L,BaseOfCode',
        'Q,ImageBase', 'L,SectionAlignment', 'L,FileAlignment',
        'H,MajorOperatingSystemVersion', 'H,MinorOperatingSystemVersion',
        'H,MajorImageVersion', 'H,MinorImageVersion',
        'H,MajorSubsystemVersion', 'H,MinorSubsystemVersion',
        'L,Reserved1', 'L,SizeOfImage', 'L,SizeOfHeaders',
        'L,CheckSum', 'H,Subsystem', 'H,DllCharacteristics',
        'Q,SizeOfStackReserve', 'Q,SizeOfStackCommit',
        'Q,SizeOfHeapReserve', 'Q,SizeOfHeapCommit',
        'L,LoaderFlags', 'L,NumberOfRvaAndSizes' ))

    IMAGE_NT_HEADERS_FORMAT = (u'IMAGE_NT_HEADERS', ('L,Signature',))
    
    IMAGE_OS2_HEADERS_FORMAT = (u'IMAGE_NT_HEADERS', ('H,Signature',))
        
    IMAGE_SECTION_HEADER_FORMAT = (u'IMAGE_SECTION_HEADER',
        ('8s,Name', 'L,Misc,Misc_PhysicalAddress,Misc_VirtualSize',
        'L,VirtualAddress', 'L,SizeOfRawData', 'L,PointerToRawData',
        'L,PointerToRelocations', 'L,PointerToLinenumbers',
        'H,NumberOfRelocations', 'H,NumberOfLinenumbers',
        'L,Characteristics'))

    IMAGE_DELAY_IMPORT_DESCRIPTOR_FORMAT = (u'IMAGE_DELAY_IMPORT_DESCRIPTOR',
        ('L,grAttrs', 'L,szName', 'L,phmod', 'L,pIAT', 'L,pINT',
        'L,pBoundIAT', 'L,pUnloadIAT', 'L,dwTimeStamp'))

    IMAGE_IMPORT_DESCRIPTOR_FORMAT = (u'IMAGE_IMPORT_DESCRIPTOR',
        ('L,OriginalFirstThunk,Characteristics',
        'L,TimeDateStamp', 'L,ForwarderChain', 'L,Name', 'L,FirstThunk'))

    IMAGE_EXPORT_DIRECTORY_FORMAT = (u'IMAGE_EXPORT_DIRECTORY',
        ('L,Characteristics',
        'L,TimeDateStamp', 'H,MajorVersion', 'H,MinorVersion', 'L,Name',
        'L,Base', 'L,NumberOfFunctions', 'L,NumberOfNames',
        'L,AddressOfFunctions', 'L,AddressOfNames', 'L,AddressOfNameOrdinals'))

    IMAGE_RESOURCE_DIRECTORY_FORMAT = (u'IMAGE_RESOURCE_DIRECTORY',
        ('L,Characteristics',
        'L,TimeDateStamp', 'H,MajorVersion', 'H,MinorVersion',
        'H,NumberOfNamedEntries', 'H,NumberOfIdEntries'))

    IMAGE_RESOURCE_DIRECTORY_ENTRY_FORMAT = (u'IMAGE_RESOURCE_DIRECTORY_ENTRY',
        ('L,Name', 'L,OffsetToData'))
            
    IMAGE_RESOURCE_DATA_ENTRY_FORMAT = (u'IMAGE_RESOURCE_DATA_ENTRY',
        ('L,OffsetToData', 'L,Size', 'L,CodePage', 'L,Reserved'))
    
    VS_VERSIONINFO_FORMAT = (u'VS_VERSIONINFO',
        ('H,Length', 'H,ValueLength', 'H,Type' ))
    
    VS_FIXEDFILEINFO_FORMAT = (u'VS_FIXEDFILEINFO',
        ('L,Signature', 'L,StrucVersion', 'L,FileVersionMS', 'L,FileVersionLS',
         'L,ProductVersionMS', 'L,ProductVersionLS', 'L,FileFlagsMask', 'L,FileFlags',
         'L,FileOS', 'L,FileType', 'L,FileSubtype', 'L,FileDateMS', 'L,FileDateLS'))
    
    StringFileInfo_FORMAT = (u'StringFileInfo',
        ('H,Length', 'H,ValueLength', 'H,Type' ))
    
    StringTable_FORMAT = (u'StringTable',
        ('H,Length', 'H,ValueLength', 'H,Type' ))
    
    String_FORMAT = (u'String',
        ('H,Length', 'H,ValueLength', 'H,Type' ))
    
    Var_FORMAT = (u'Var', ('H,Length', 'H,ValueLength', 'H,Type' ))

    IMAGE_THUNK_DATA_FORMAT = (u'IMAGE_THUNK_DATA',
        ('L,ForwarderString,Function,Ordinal,AddressOfData',))

    IMAGE_THUNK_DATA64_FORMAT = (u'IMAGE_THUNK_DATA',
        ('Q,ForwarderString,Function,Ordinal,AddressOfData',))

    IMAGE_DEBUG_DIRECTORY_FORMAT = (u'IMAGE_DEBUG_DIRECTORY',
        ('L,Characteristics', 'L,TimeDateStamp', 'H,MajorVersion',
        'H,MinorVersion', 'L,Type', 'L,SizeOfData', 'L,AddressOfRawData',
        'L,PointerToRawData'))
    
    IMAGE_BASE_RELOCATION_FORMAT = (u'IMAGE_BASE_RELOCATION',
        ('L,VirtualAddress', 'L,SizeOfBlock') )

    IMAGE_TLS_DIRECTORY_FORMAT = (u'IMAGE_TLS_DIRECTORY',
        ('L,StartAddressOfRawData', 'L,EndAddressOfRawData',
        'L,AddressOfIndex', 'L,AddressOfCallBacks',
        'L,SizeOfZeroFill', 'L,Characteristics' ) )

    IMAGE_TLS_DIRECTORY64_FORMAT = (u'IMAGE_TLS_DIRECTORY',
        ('Q,StartAddressOfRawData', 'Q,EndAddressOfRawData',
        'Q,AddressOfIndex', 'Q,AddressOfCallBacks',
        'L,SizeOfZeroFill', 'L,Characteristics' ) )

    IMAGE_BOUND_IMPORT_DESCRIPTOR_FORMAT = (u'IMAGE_BOUND_IMPORT_DESCRIPTOR',
        ('L,TimeDateStamp', 'H,OffsetModuleName', 'H,NumberOfModuleForwarderRefs'))

    IMAGE_BOUND_FORWARDER_REF_FORMAT = (u'IMAGE_BOUND_FORWARDER_REF',
        ('L,TimeDateStamp', 'H,OffsetModuleName', 'H,Reserved') )

    IMAGE_SECURITY_DIRECTORY_FORMAT = (u'IMAGE_SECURITY_DIRECTORY',
        ('L,Length', 'H,Revision', 'H,CertificateType'))
    
#    def __settimeout(self):
#        print u'Time out...'
#        self._timeout = True
#        
#    def __timeout(self):
#        raise PEFormatError(u'It take too long to parse this file.')
        
    def __init__(self, filepath, fast_load=False):
        self.__fileobj = open(filepath, 'rb')
        self.filepath = filepath
        self.filesize = os.path.getsize(filepath)
        self.sections = []
        self.__warnings = []
        self.PE_TYPE = None
        # This list will keep track of all the structures created.
        # That will allow for an easy iteration through the list
        # in order to save the modifications made
        self.structures = []
#        self.timer = None
#        self._timeout = False
        self.__versioninfo = None
        self.fast_load = fast_load
#        self.__parse(self.__fileobj, self.filesize, fast_load)
    
    def __unpack_data(self, format, data=None, file_offset=0):
        """Apply structure format to raw data.
        
        Returns and unpacked structure object if successful, None otherwise.
        """
        structure = Structure(format, file_offset=file_offset)
        if data is None:
            data = self.__data
        try:
            structure.unpack(data)
        except PEFormatError, e:
            self.__warnings.append(
                u'Corrupt header "%s" at file offset %d. Exception: %s' % (
                    format[0], file_offset, unicode(e)))
            return None
        self.structures.append(structure)
        return structure
    
    def parse(self):
        """Parse a Portable Executable file.
        
        Loads a PE file, parsing all its structures and making them available
        through the instance's attributes.
        
        Fixed: Don't load the whole file to memory
        """
        self.__data = Data(self.__fileobj, self.filesize)
        self.DOS_HEADER = self.__unpack_data(
            self.IMAGE_DOS_HEADER_FORMAT, file_offset=0)
        if not self.DOS_HEADER or self.DOS_HEADER.e_magic != IMAGE_DOS_SIGNATURE:
            raise PEFormatError(u'DOS Header magic not found.')
        # OC Patch:
        # Check for sane value in e_lfanew
        if self.DOS_HEADER.e_lfanew > len(self.__data):
            raise PEFormatError(u'Invalid e_lfanew value, probably not a PE file')
        
        nt_headers_offset = self.DOS_HEADER.e_lfanew
        self.NT_HEADERS = self.__unpack_data(
            self.IMAGE_NT_HEADERS_FORMAT, file_offset=nt_headers_offset)
        # We better check the signature right here, before the file screws
        # around with sections:
        # OC Patch:
        # Some malware will cause the Signature value to not exist at all
        if not self.NT_HEADERS or not self.NT_HEADERS.Signature:
            raise PEFormatError(u'NT Headers not found.')
        if self.NT_HEADERS.Signature != IMAGE_NT_SIGNATURE:
#            self.NT_HEADERS = self.__unpack_data(
#                self.IMAGE_OS2_HEADERS_FORMAT,
#                self.__data[nt_headers_offset:],
#                file_offset = nt_headers_offset)
#            if self.NT_HEADERS.Signature != IMAGE_OS2_SIGNATURE:
            raise PEFormatError(u'Invalid NT Headers signature.')
        self.FILE_HEADER = self.__unpack_data(
            self.IMAGE_FILE_HEADER_FORMAT, file_offset=nt_headers_offset+4)
        image_flags = self.retrieve_flags(IMAGE_CHARACTERISTICS, 'IMAGE_FILE_')
        if not self.FILE_HEADER:
            raise PEFormatError(u'File Header missing')

        # Set the image's flags according the the Characteristics member
        self.set_flags(self.FILE_HEADER, self.FILE_HEADER.Characteristics, image_flags)
        optional_header_offset = nt_headers_offset + 4 + self.FILE_HEADER.size

        # Note: location of sections can be controlled from PE header:
        sections_offset = optional_header_offset + self.FILE_HEADER.SizeOfOptionalHeader

        self.OPTIONAL_HEADER = self.__unpack_data(
            self.IMAGE_OPTIONAL_HEADER_FORMAT, file_offset=optional_header_offset)

        # According to solardesigner's findings for his
        # Tiny PE project, the optional header does not
        # need fields beyond "Subsystem" in order to be
        # loadable by the Windows loader (given that zeroes
        # are acceptable values and the header is loaded
        # in a zeroed memory page)
        # If trying to parse a full Optional Header fails
        # we try to parse it again with some 0 padding
        MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69
        
#        if (self.OPTIONAL_HEADER is None \
#                and len(self.__data[optional_header_offset:]) >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE):
        if (self.OPTIONAL_HEADER is None \
                and (len(self.__data) - optional_header_offset >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE)):
            # Add enough zeroes to make up for the unused fields
            padding_length = 128
            # Create padding
            padded_data = self.__data[optional_header_offset:] + ('\0' * padding_length)
            self.OPTIONAL_HEADER = self.__unpack_data(
                self.IMAGE_OPTIONAL_HEADER_FORMAT, padded_data, 
                file_offset=optional_header_offset)
         
        # Check the Magic in the OPTIONAL_HEADER and set the PE file
        # type accordingly
        if self.OPTIONAL_HEADER is not None:
            if self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE:
                self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE
            elif self.OPTIONAL_HEADER.Magic == OPTIONAL_HEADER_MAGIC_PE_PLUS:
                self.PE_TYPE = OPTIONAL_HEADER_MAGIC_PE_PLUS
                self.OPTIONAL_HEADER = self.__unpack_data(
                    self.IMAGE_OPTIONAL_HEADER64_FORMAT, 
                    file_offset=optional_header_offset)

                # Again, as explained above, we try to parse
                # a reduced form of the Optional Header which
                # is still valid despite not including all
                # structure members
                MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE = 69 + 4

                if (self.OPTIONAL_HEADER is None and 
                    (len(self.__data) - optional_header_offset) >= MINIMUM_VALID_OPTIONAL_HEADER_RAW_SIZE):
                    padding_length = 128
                    padded_data = self.__data[optional_header_offset:] + (
                                                    '\0' * padding_length)
                    self.OPTIONAL_HEADER = self.__unpack_data(
                        self.IMAGE_OPTIONAL_HEADER64_FORMAT,
                        padded_data, file_offset=optional_header_offset)
        
        # OC Patch:
        # Die gracefully if there is no OPTIONAL_HEADER field
        # 975440f5ad5e2e4a92c4d9a5f22f75c1
        if self.PE_TYPE is None or self.OPTIONAL_HEADER is None:
            raise PEFormatError(u'No Optional Header found, invalid PE32 or PE32+ file')
            
        dll_characteristics_flags = self.retrieve_flags(DLL_CHARACTERISTICS, 'IMAGE_DLL_CHARACTERISTICS_')
        # Set the Dll Characteristics flags according the the DllCharacteristics member
        self.set_flags(self.OPTIONAL_HEADER, self.OPTIONAL_HEADER.DllCharacteristics,
                       dll_characteristics_flags)

        self.OPTIONAL_HEADER.DATA_DIRECTORY = []
        #offset = (optional_header_offset + self.FILE_HEADER.SizeOfOptionalHeader)
        offset = (optional_header_offset + self.OPTIONAL_HEADER.size)
        self.NT_HEADERS.FILE_HEADER = self.FILE_HEADER
        self.NT_HEADERS.OPTIONAL_HEADER = self.OPTIONAL_HEADER
        
        # The NumberOfRvaAndSizes is sanitized to stay within 
        # reasonable limits so can be casted to an int
        if self.OPTIONAL_HEADER.NumberOfRvaAndSizes > 0x10:
            self.__warnings.append(
                u'Suspicious NumberOfRvaAndSizes in the Optional Header. ' +
                u'Normal values are never larger than 0x10, the value is: 0x%x' %
                    self.OPTIONAL_HEADER.NumberOfRvaAndSizes)
        for i in xrange(int(0x7fffffff & self.OPTIONAL_HEADER.NumberOfRvaAndSizes)):
            if len(self.__data) - offset == 0:
                break
            if len(self.__data) - offset < 8:
                data = self.__data[offset:] + '\0' * 8
            else:
                data = self.__data
#                data = self.__data[offset:]
            dir_entry = self.__unpack_data(
                self.IMAGE_DATA_DIRECTORY_FORMAT, data, file_offset=offset)
            if dir_entry is None:
                break
            # Would fail if missing an entry
            # 1d4937b2fa4d84ad1bce0309857e70ca offending sample
            try:
                dir_entry.name = DIRECTORY_ENTRY[i]
            except (KeyError, AttributeError):
                break
            offset += dir_entry.size
            self.OPTIONAL_HEADER.DATA_DIRECTORY.append(dir_entry)
            # If the offset goes outside the optional header,
            # the loop is broken, regardless of how many directories
            # NumberOfRvaAndSizes says there are
            # We assume a normally sized optional header, hence that we do
            # a size property instead of reading SizeOfOptionalHeader.
            # Then we add a default number of drectories times their size,
            # if we go beyond that, we assume the number of directories
            # is wrong and stop processing
            if offset >= (optional_header_offset + self.OPTIONAL_HEADER.size + 8 * 16):
                break
        offset = self.parse_sections(sections_offset)
        # OC Patch:
        # There could be a problem if there are no raw data sections
        # greater than 0
        # fc91013eb72529da005110a3403541b6 example
        # Should this throw an exception in the minimum header offset
        # can't be found?
        rawDataPointers = [s.PointerToRawData for s in self.sections if s.PointerToRawData>0]
        if len(rawDataPointers) > 0:
            lowest_section_offset = min(rawDataPointers)
        else:
            lowest_section_offset = None
        try:
            if not lowest_section_offset or lowest_section_offset<offset:
                self.header = self.__data[:offset]
            else:
                self.header = self.__data[:lowest_section_offset]
        except IndexError, e:
            raise PEFormatError(u'Bad header format, error: %s' % e)
        # Check whether the entry point lies within a section
        if self.get_section_by_rva(self.OPTIONAL_HEADER.AddressOfEntryPoint) is not None:
            # Check whether the entry point lies within the file
            ep_offset = self.get_offset_from_rva(self.OPTIONAL_HEADER.AddressOfEntryPoint)
            if ep_offset > len(self.__data):
                self.__warnings.append(
                    u'Possibly corrupt file. AddressOfEntryPoint lies outside the file. ' +
                    u'AddressOfEntryPoint: 0x%x' %
                    self.OPTIONAL_HEADER.AddressOfEntryPoint)
        else:
            self.__warnings.append(
                u'AddressOfEntryPoint lies outside the sections\' boundaries. ' +
                u'AddressOfEntryPoint: 0x%x' %
                self.OPTIONAL_HEADER.AddressOfEntryPoint )
        if not self.fast_load:
            self.parse_data_directories()
    
    @property        
    def warnings(self):
        """Return the list of warnings.
        
        Non-critical problems found when parsing the PE file are
        appended to a list of warnings. This method returns the
        full list.
        """
        return self.__warnings
        
    def show_warnings(self):
        """Print the list of warnings.
        
        Non-critical problems found when parsing the PE file are
        appended to a list of warnings. This method prints the
        full list to standard output.
        """
        for warning in self.__warnings:
            print u'>', warning

    def full_load(self):
        """Process the data directories.
        
        This mathod will load the data directories which might not have
        been loaded if the "fast_load" option was used.
        """
        self.parse_data_directories()
        
    def write(self, filename=None):
        """Write the PE file.
        
        This function will process all headers and components
        of the PE file and include all changes made (by just
        assigning to attributes in the PE objects) and write
        the changes back to a file whose name is provided as
        an argument. The filename is optional.
        The data to be written to the file will be returned
        as a 'str' object.
        """
        file_data = list(self.__data)
        for struct in self.structures:
            struct_data = list(struct.__pack__())
            offset = struct.get_file_offset()
            file_data[offset:offset+len(struct_data)] = struct_data
        if hasattr(self, 'VS_VERSIONINFO'):
            if hasattr(self, 'FileInfo'):
                for entry in self.FileInfo:
                    if hasattr(entry, 'StringTable'):
                        for st_entry in entry.StringTable:
                            for key, entry in st_entry.entries.items():
                                offsets = st_entry.entries_offsets[key]
                                lengths = st_entry.entries_lengths[key]
                                if len( entry ) > lengths[1]:
                                    uc = zip(
                                            list(entry[:lengths[1]]), ['\0'] * lengths[1] )
                                    l = list()
                                    map(l.extend, uc)
                                    file_data[ 
                                        offsets[1] : offsets[1] + lengths[1]*2] = l
                                else:
                                    uc = zip(
                                            list(entry), ['\0'] * len(entry))
                                    l = list()
                                    map(l.extend, uc)
                                    file_data[ 
                                        offsets[1] : offsets[1] + len(entry)*2] = l
                                    remainder = lengths[1] - len(entry)
                                    file_data[ 
                                        offsets[1] + len(entry)*2 : 
                                        offsets[1] + lengths[1]*2 ] = [
                                            u'\0' ] * remainder*2
        new_file_data = ''.join( [ chr(ord(c)) for c in file_data ])
        if filename:
            f = file(filename, 'wb+')
            f.write(new_file_data)
            f.close()
        return new_file_data
                
    def parse_sections(self, offset):
        """Fetch the PE file sections.
        
        The sections will be readily available in the "sections" attribute.
        Its attributes will contain all the section information plus "data"
        a buffer containing the section's data.
        
        The "Characteristics" member will be processed and attributes 
        representing the section characteristics (with the 'IMAGE_SCN_'
        string trimmed from the constant's names) will be added to the
        section instance.
        
        Refer to the SectionStructure class for additional info.
        """
        self.sections = []
        for i in xrange(self.FILE_HEADER.NumberOfSections):
            section = SectionStructure(self.IMAGE_SECTION_HEADER_FORMAT)
            section_offset = offset + section.size * i
            section.file_offset = section_offset
            section.unpack(self.__data)
            self.structures.append(section)
            if section.SizeOfRawData > len(self.__data):
                self.__warnings.append(u'Error parsing section %d. ' % i +
                    u'SizeOfRawData is larger than file.')
            if section.PointerToRawData > len(self.__data):
                self.__warnings.append(u'Error parsing section %d. ' % i +
                    u'PointerToRawData points beyond the end of the file.')
            if section.Misc_VirtualSize > 0x10000000:
                self.__warnings.append(u'Suspicious value found parsing section %d. ' % i +
                    u'VirtualSize is extremely large > 256MiB.')
            if section.VirtualAddress > 0x10000000:
                self.__warnings.append(u'Suspicious value found parsing section %d. ' % i +
                    u'VirtualAddress is beyond 0x10000000.')
            # Some packer used a non-aligned PointerToRawData in the sections,
            # which causes several common tools not to load the section data
            # properly as they blindly read from the indicated offset.
            # It seems that Windows will round the offset down to the largest
            # offset multiple of FileAlignment which is smaller than
            # PointerToRawData. The following code will do the same.
            
            #alignment = self.OPTIONAL_HEADER.FileAlignment
            section_data_start = section.PointerToRawData
            if (self.OPTIONAL_HEADER.FileAlignment != 0 \
                and (section.PointerToRawData % self.OPTIONAL_HEADER.FileAlignment) != 0):
                self.__warnings.append((u'Error parsing section %d. ' % i) +
                    u'Suspicious value for FileAlignment in the Optional Header. ' +
                    u'Normally the PointerToRawData entry of the sections\' structures ' +
                    u'is a multiple of FileAlignment, this might imply the file ' +
                    u'is trying to confuse tools which parse this incorrectly')
            section_data_end = section_data_start+section.SizeOfRawData
            section.set_data(Data(self.__fileobj, section.SizeOfRawData, section.PointerToRawData))
#            section.set_data(self.__data[section_data_start : section_data_end])
            section_flags = self.retrieve_flags(SECTION_CHARACTERISTICS, 'IMAGE_SCN_')
            # Set the section's flags according the the Characteristics member
            self.set_flags(section, section.Characteristics, section_flags)
            if (section.__dict__.get('IMAGE_SCN_MEM_WRITE', False) \
                    and section.__dict__.get('IMAGE_SCN_MEM_EXECUTE', False)):
                self.__warnings.append((u'Suspicious flags set for section %d. ' % i) +
                    u'Both IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE are set.' +
                    u'This might indicate a packed executable.')
            self.sections.append(section)
        if self.FILE_HEADER.NumberOfSections > 0 and self.sections:
            return offset + self.sections[0].size * self.FILE_HEADER.NumberOfSections
        else:
            return offset

    def retrieve_flags(self, flag_dict, flag_filter):
        """Read the flags from a dictionary and return them in a usable form.
        
        Will return a list of (flag, value) for all flags in "flag_dict"
        matching the filter "flag_filter".
        """
        return [(f[0], f[1]) for f in flag_dict.items() \
                    if isinstance(f[0], basestring) and f[0].startswith(flag_filter)]

    def set_flags(self, obj, flag_field, flags):
        """Will process the flags and set attributes in the object accordingly.
        
        The object "obj" will gain attritutes named after the flags provided in
        "flags" and valued True/False, matching the results of applyin each
        flag value from "flags" to flag_field.
        """
        for flag in flags:
            if flag[1] & flag_field:
                setattr(obj, flag[0], True)
            else:
                setattr(obj, flag[0], False)
                
    def parse_data_directories(self):
        """Parse and process the PE file's data directories."""
        directory_parsing = (
                (u'IMAGE_DIRECTORY_ENTRY_IMPORT', self.parse_import_directory),
                (u'IMAGE_DIRECTORY_ENTRY_EXPORT', self.parse_export_directory),
                (u'IMAGE_DIRECTORY_ENTRY_RESOURCE', self.parse_resources_directory), 
                (u'IMAGE_DIRECTORY_ENTRY_DEBUG', self.parse_debug_directory),
#                (u'IMAGE_DIRECTORY_ENTRY_BASERELOC', self.parse_relocations_directory),
                (u'IMAGE_DIRECTORY_ENTRY_TLS', self.parse_directory_tls),
                (u'IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT', self.parse_delay_import_directory),
                (u'IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT', self.parse_directory_bound_imports),
                (u'IMAGE_DIRECTORY_ENTRY_SECURITY', self.parse_security_directory), #add by mk2
        )
        for entry in directory_parsing:
            # OC Patch:
            if hasattr(self, entry[0][6:]):
                continue
            try:
                dir_entry = self.OPTIONAL_HEADER.DATA_DIRECTORY[
                    DIRECTORY_ENTRY[entry[0]]]
#                if entry[0] == u'IMAGE_DIRECTORY_ENTRY_RESOURCE':
#                    self.timer = Timer(60, self.__settimeout) #1 minutes
#                    self.timer.start()
            except IndexError:
                break
            if dir_entry.VirtualAddress:
                print entry[0][6:]
                value = entry[1](dir_entry.VirtualAddress, dir_entry.Size)
                if value:
                    setattr(self, entry[0][6:], value)
#        if self.timer:
#            self.timer.cancel()
        
    def parse_directory_bound_imports(self, rva, size):
        """"""
#        bnd_descr = Structure(self.IMAGE_BOUND_IMPORT_DESCRIPTOR_FORMAT)
#        bnd_descr_size = bnd_descr.size
        start = rva
        bound_imports = []
        while True:
            bnd_descr = self.__unpack_data(
                self.IMAGE_BOUND_IMPORT_DESCRIPTOR_FORMAT,
#                   self.__data[rva:rva+bnd_descr_size],
                   file_offset=rva)
            if bnd_descr is None:
                # If can't parse directory then silently return.
                # This directory does not necesarily have to be valid to
                # still have a valid PE file
                self.__warnings.append(u'The Bound Imports directory exists but can\'t be parsed.')
                return
            if bnd_descr.all_zeroes:
                break
            rva += bnd_descr.size
            forwarder_refs = []
            for idx in xrange(bnd_descr.NumberOfModuleForwarderRefs):
                # Both structures IMAGE_BOUND_IMPORT_DESCRIPTOR and
                # IMAGE_BOUND_FORWARDER_REF have the same size.
                bnd_frwd_ref = self.__unpack_data(
                    self.IMAGE_BOUND_FORWARDER_REF_FORMAT, file_offset=rva)
                # OC Patch:
                if not bnd_frwd_ref:
                    raise PEFormatError(u"IMAGE_BOUND_FORWARDER_REF cannot be read")
                rva += bnd_frwd_ref.size
                name_str =  self.get_string_from_data(start+bnd_frwd_ref.OffsetModuleName, self.__data)
                if not name_str:
                    break
                forwarder_refs.append(BoundImportRefData(struct=bnd_frwd_ref, name=name_str))
            name_str = self.get_string_from_data(start+bnd_descr.OffsetModuleName, self.__data)
            if not name_str:
                break
            bound_imports.append(BoundImportDescData(struct=bnd_descr, name=name_str, entries=forwarder_refs))
        return bound_imports

    def parse_directory_tls(self, rva, size):
        """"""
        if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
            format = self.IMAGE_TLS_DIRECTORY_FORMAT
        elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
            format = self.IMAGE_TLS_DIRECTORY64_FORMAT
        tls_struct = self.__unpack_data(format, file_offset=self.get_offset_from_rva(rva))
        if not tls_struct:
            return None
        return TlsData(struct=tls_struct)

    def parse_relocations_directory(self, rva, size):
        """"""
        rlc = Structure(self.IMAGE_BASE_RELOCATION_FORMAT)
        rlc_size = rlc.size
        end = rva + size
        relocations = []
        while rva < end:
            # OC Patch:
            # Malware that has bad rva entries will cause an error.
            # Just continue on after an exception
            try:
                rlc = self.__unpack_data(self.IMAGE_BASE_RELOCATION_FORMAT, 
                                           file_offset=self.get_offset_from_rva(rva))
            except PEFormatError:
                self.__warnings.append(u'Invalid relocation information. Can\'t read data at RVA: 0x%x' % rva)
                rlc = None
            if not rlc:
                break
            reloc_entries = self.parse_relocations(rva+rlc_size, rlc.VirtualAddress, rlc.SizeOfBlock-rlc_size)
            relocations.append(BaseRelocationData(struct=rlc, entries=reloc_entries))
            if not rlc.SizeOfBlock:
                break
#            print rva, rlc.SizeOfBlock, end
            rva += rlc.SizeOfBlock
        return relocations

    def parse_relocations(self, data_rva, rva, size):
        """"""
        data = self.get_data(data_rva, size)
        entries = []
        for idx in xrange(len(data)/2):
            word = struct.unpack('<H', data[idx*2:(idx+1)*2])[0]
            reloc_type = (word>>12)
            reloc_offset = (word&0x0fff)
            entries.append(RelocationData(type=reloc_type, rva=reloc_offset+rva))
        return entries

    def parse_debug_directory(self, rva, size):
        """"""
        dbg = Structure(self.IMAGE_DEBUG_DIRECTORY_FORMAT)
        dbg_size = dbg.size
        debug = []
        for idx in xrange(size/dbg_size):
#            try:
#                data = self.get_data(rva+dbg_size*idx, dbg_size)
#            except PEFormatError, e:
#                self.__warnings.append(u'Invalid debug information. Can\'t read data at RVA: 0x%x' % rva)
#                return None
            dbg = self.__unpack_data(
                self.IMAGE_DEBUG_DIRECTORY_FORMAT, file_offset=self.get_offset_from_rva(rva+dbg_size*idx))
            if not dbg:
                return None
            debug.append(DebugData(struct=dbg))
        return debug
        
    def parse_security_directory(self, rva, size):
        """Parse The Attribute Certificate Table
        """
        certificate = Structure(self.IMAGE_SECURITY_DIRECTORY_FORMAT)
        header_size = certificate.size
        offset = rva + header_size
        header_data = self.__data[rva:offset]
        try:
            length, revision, certificate_type = struct.unpack('LHH', header_data)
        except struct.error:
            self.__warnings.append(u'Invalid security directory. Can\'t read data at RVA: 0x%x' % rva)
            return None
        if length <= 0:
            return None
        try:
            certificate_data = self.__data[offset:offset+length]#Data(self.__fileobj, length, offset)
        except IndexError, e:
            self.__warnings.append(u'Invalid security directory. ' + 
                                   u'Can\'t read data at offset: 0x%x, error: %s' % (offset, e))
            return None
        try:
            sign_data = asn1.parse(certificate_data)
            if isinstance(sign_data, asn1.Sequence) \
                and sign_data and unicode(sign_data[0]) == u'SignedData':
                self.SignedData = signeddata.SignedData(sign_data[1][0])
                self.SignedData.Revision = REVISION[revision]
                self.SignedData.CertificateType = CERTIFICATE_TYPE[certificate_type]
                return self.SignedData
            else:
                self.__warnings.append(u'SignedData is bad format: %s' % sign_data)
                return None
        except ValueError, e:
            self.__warnings.append(u'SignedData error: %s' % e)
            return None
                        
    def parse_resources_directory(self, rva, size=0, base_rva=None, level=0):
        """Parse the resources directory.
        
        Given the rva of the resources directory, it will process all
        its entries.
        
        The root will have the corresponding member of its structure,
        IMAGE_RESOURCE_DIRECTORY plus 'entries', a list of all the
        entries in the directory.
        
        Those entries will have, correspondingly, all the structure's
        members (IMAGE_RESOURCE_DIRECTORY_ENTRY) and an additional one,
        "directory", pointing to the IMAGE_RESOURCE_DIRECTORY structure
        representing upper layers of the tree. This one will also have
        an 'entries' attribute, pointing to the 3rd, and last, level.
        Another directory with more entries. Those last entries will
        have a new atribute (both 'leaf' or 'data_entry' can be used to
        access it). This structure finally points to the resource data.
        All the members of this structure, IMAGE_RESOURCE_DATA_ENTRY,
        are available as its attributes.
        """
        # OC Patch:
#        if self._timeout:
#            self.__warnings.append(u'Take too long to get the resource...')
#            return None
#        print 'rva: %d, size: %d, base_rva: %s, level: %d' % (rva, size, base_rva, level)
        original_rva = rva
        if base_rva is None:
            base_rva = rva
        resources_section = self.get_section_by_rva(rva)
        try:
            # If the RVA is invalid all would blow up. Some EXEs seem to be
            # specially nasty and have an invalid RVA.
            resource_dir = self.__unpack_data(self.IMAGE_RESOURCE_DIRECTORY_FORMAT,
                                          file_offset=self.get_offset_from_rva(rva))
        except PEFormatError, e:
            self.__warnings.append(
                'Invalid resources directory. Can\'t read ' +
                'directory data at RVA: 0x%x' % rva)
            return None
        # Get the resource directory structure, that is, the header
        # of the table preceding the actual entries
#        resource_dir = self.__unpack_data(self.IMAGE_RESOURCE_DIRECTORY_FORMAT,
#                                          file_offset=self.get_offset_from_rva(rva))
        if resource_dir is None:
            # If can't parse resources directory then silently return.
            # This directory does not necesarily have to be valid to
            # still have a valid PE file
            self.__warnings.append(u'Invalid resources directory. Can\'t parse directory data at RVA: 0x%x' % rva)
            return None
        dir_entries = []
        # Advance the rva to the positon immediately following the directory
        # table header and pointing to the first entry in the table
        rva += resource_dir.size
        number_of_entries = (resource_dir.NumberOfNamedEntries + resource_dir.NumberOfIdEntries)
        strings_to_postprocess = []
        for idx in xrange(number_of_entries):
            res = self.parse_resource_entry(rva)
            if res is None:
                self.__warnings.append(u'Error parsing the resources directory, Entry %d is invalid, RVA = 0x%x. ' % (idx, rva))
                break
            entry_name = None
            entry_id = None
            # If all named entries have been processed, only Id ones remain
            if idx >= resource_dir.NumberOfNamedEntries:
                entry_id = res.Name
            else:
                ustr_offset = base_rva + res.NameOffset
                try:
                    #entry_name = self.get_string_u_at_rva(ustr_offset, max_length=16)
                    entry_name = UnicodeStringWrapperPostProcessor(self, ustr_offset)
                    strings_to_postprocess.append(entry_name)
                except PEFormatError, e:
                    self.__warnings.append(u'Error parsing the resources directory, ' +
                                u'attempting to read entry name. ' +
                                u'Can\'t read unicode string at offset 0x%x' % 
                                (ustr_offset))
            if res.DataIsDirectory:
                # OC Patch:
                # One trick malware can do is to recursively reference
                # the next directory. This causes hilarity to ensue when
                # trying to parse everything correctly.
                # If the original RVA given to this function is equal to
                # the next one to parse, we assume that it's a trick.
                # Instead of raising a PEFormatError this would skip some
                # reasonable data so we just break.
                # 9ee4d0a0caf095314fd7041a3e4404dc is the offending sample
                if original_rva == (base_rva + res.OffsetToDirectory):
                    break
                else:
                    entry_directory = self.parse_resources_directory(
                        base_rva+res.OffsetToDirectory, base_rva=base_rva, level=level+1)
                if not entry_directory:
                    break
                dir_entries.append(ResourceDirEntryData(struct=res,
                        name=entry_name, id=entry_id, directory=entry_directory))
            else:
                struct = self.parse_resource_data_entry(base_rva+res.OffsetToDirectory)
                if struct:
                    entry_data = ResourceDataEntryData(struct=struct,
                        lang=res.Name & 0xff, sublang = (res.Name>>8) & 0xff)
                    dir_entries.append(ResourceDirEntryData(struct=res,
                            name=entry_name, id=entry_id, data=entry_data))
                else:
                    break
            # Check if this entry contains version information
            if level == 0 and res.Id == RESOURCE_TYPE['RT_VERSION']:
                if len(dir_entries) > 0:
                    last_entry = dir_entries[-1]
                rt_version_struct = None
                try:
                    rt_version_struct = last_entry.directory.entries[0].directory.entries[0].data.struct
                except:
                    # Maybe a malformed directory structure...?
                    # Lets ignore it
                    pass
                if rt_version_struct is not None:
                    self.parse_version_information(rt_version_struct)
            rva += res.size
#        string_rvas = [s.get_rva() for s in strings_to_postprocess]
#        string_rvas.sort()
        for idx, s in enumerate(strings_to_postprocess):
            s.render_pascal_16()
        resource_directory_data = ResourceDirData(struct=resource_dir, entries=dir_entries)
        return resource_directory_data

    def parse_resource_data_entry(self, rva):
        """Parse a data entry from the resources directory."""
#        try:
#            # If the RVA is invalid all would blow up. Some EXEs seem to be
#            # specially nasty and have an invalid RVA.
#            data = self.get_data(rva)
#        except PEFormatError, excp:
#            self.__warnings.append(
#                'Error parsing a resource directory data entry, ' +
#                'the RVA is invalid: 0x%x' % (rva))
#            return None
        data_entry = self.__unpack_data(self.IMAGE_RESOURCE_DATA_ENTRY_FORMAT,
                                          file_offset=self.get_offset_from_rva(rva))
        return data_entry

    def parse_resource_entry(self, rva):
        """Parse a directory entry from the resources directory."""
        resource = self.__unpack_data(self.IMAGE_RESOURCE_DIRECTORY_ENTRY_FORMAT,
                                        file_offset=self.get_offset_from_rva(rva))
        if resource is None:
            return None
        #resource.NameIsString = (resource.Name & 0x80000000L) >> 31
        resource.NameOffset = resource.Name & 0x7FFFFFFFL
        resource.__pad = resource.Name & 0xFFFF0000L
        resource.Id = resource.Name & 0x0000FFFFL
        resource.DataIsDirectory = (resource.OffsetToData & 0x80000000L) >> 31
        resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFFL
        return resource

    def parse_version_information(self, version_struct):
        """Parse version information structure.
        
        The date will be made available in three attributes of the PE object.
        
        VS_VERSIONINFO     will contain the first three fields of the main structure:
            'Length', 'ValueLength', and 'Type'
            
        VS_FIXEDFILEINFO    will hold the rest of the fields, accessible as sub-attributes:
            'Signature', 'StrucVersion', 'FileVersionMS', 'FileVersionLS',
            'ProductVersionMS', 'ProductVersionLS', 'FileFlagsMask', 'FileFlags',
            'FileOS', 'FileType', 'FileSubtype', 'FileDateMS', 'FileDateLS'
            
        FileInfo    is a list of all StringFileInfo and VarFileInfo structures.
        
        StringFileInfo structures will have a list as an attribute named 'StringTable'
        containing all the StringTable structures. Each of those structures contains a 
        dictionary 'entries' with all the key/value version information string pairs.
        
        VarFileInfo structures will have a list as an attribute named 'Var' containing
        all Var structures. Each Var structure will have a dictionary as an attribute
        named 'entry' which will contain the name and value of the Var.
        """
        # Retrieve the data for the version info resource
        start_offset = self.get_offset_from_rva(version_struct.OffsetToData)
        raw_data = self.__data[start_offset:start_offset+version_struct.Size]
        # Map the main structure and the subsequent string
        versioninfo_struct = self.__unpack_data(self.VS_VERSIONINFO_FORMAT, file_offset=start_offset)
        if versioninfo_struct is None:
            return
        ustr_offset = version_struct.OffsetToData + versioninfo_struct.size
        try:
            versioninfo_string = self.get_string_u_at_rva(ustr_offset)
        except PEFormatError, excp:
            self.__warnings.append(u'Error parsing the version information, ' +
                u'attempting to read VS_VERSION_INFO string. Can\'t ' +
                u'read unicode string at offset 0x%x' % (ustr_offset))
            versioninfo_string = None
        # If the structure does not contain the expected name, it's assumed to be invalid
        if versioninfo_string != u'VS_VERSION_INFO':
            self.__warnings.append(u'Invalid VS_VERSION_INFO block')
            return
        # Set the PE object's VS_VERSIONINFO to this one
        self.VS_VERSIONINFO = versioninfo_struct
        self.__versioninfo = {}
        # The the Key attribute to point to the unicode string identifying the structure
        self.VS_VERSIONINFO.Key = versioninfo_string
        # Process the fixed version information, get the offset and structure
        fixedfileinfo_offset = self.dword_align(versioninfo_struct.size + 2 * (len(versioninfo_string) + 1),
            version_struct.OffsetToData)
        fixedfileinfo_struct = self.__unpack_data(self.VS_FIXEDFILEINFO_FORMAT,
                                                    file_offset=start_offset+fixedfileinfo_offset)
        if not fixedfileinfo_struct:
            return
        # Set the PE object's VS_FIXEDFILEINFO to this one
        self.VS_FIXEDFILEINFO = fixedfileinfo_struct
        # Start parsing all the StringFileInfo and VarFileInfo structures
        # Get the first one
        stringfileinfo_offset = self.dword_align(fixedfileinfo_offset+fixedfileinfo_struct.size,
                                                 version_struct.OffsetToData)
        original_stringfileinfo_offset = stringfileinfo_offset
        # Set the PE object's attribute that will contain them all.
        self.FileInfo = []
        while True:
            # Process the StringFileInfo/VarFileInfo struct
            stringfileinfo_struct = self.__unpack_data(self.StringFileInfo_FORMAT, 
                                                         file_offset=start_offset+stringfileinfo_offset)
            if stringfileinfo_struct is None:
                self.__warnings.append(u'Error parsing StringFileInfo/VarFileInfo struct')
                return None
            # Get the subsequent string defining the structure.
            ustr_offset = version_struct.OffsetToData + stringfileinfo_offset + versioninfo_struct.size
            try:
                stringfileinfo_string = self.get_string_u_at_rva(ustr_offset)
            except PEFormatError, e:
                self.__warnings.append(u'Error parsing the version information, ' +
                    u'attempting to read StringFileInfo string. Can\'t ' +
                    u'read unicode string at offset 0x%x' %  (ustr_offset))
                break
            # Set such string as the Key attribute
            stringfileinfo_struct.Key = stringfileinfo_string
            # Append the structure to the PE object's list
            self.FileInfo.append(stringfileinfo_struct)
            # Parse a StringFileInfo entry
            if stringfileinfo_string == u'StringFileInfo':
                if stringfileinfo_struct.Type == 1 and stringfileinfo_struct.ValueLength == 0:
                    stringtable_offset = self.dword_align(
                        stringfileinfo_offset + stringfileinfo_struct.size + 2 * (len(stringfileinfo_string) + 1),
                        version_struct.OffsetToData)
                    stringfileinfo_struct.StringTable = []
                    # Process the String Table entries
                    while True:
                        stringtable_struct = self.__unpack_data(self.StringTable_FORMAT,
                                                                  file_offset=start_offset+stringtable_offset)
                        if not stringtable_struct:
                            break
                        ustr_offset = (version_struct.OffsetToData + stringtable_offset + 
                                                                stringtable_struct.size)
                        try:
                            stringtable_string = self.get_string_u_at_rva(ustr_offset)
                        except PEFormatError, e:
                            self.__warnings.append(
                                u'Error parsing the version information, ' +
                                u'attempting to read StringTable string. Can\'t ' +
                                u'read unicode string at offset 0x%x' % (ustr_offset))
                            break
                        stringtable_struct.LangID = stringtable_string
                        if stringtable_string and stringtable_string != u'0':
                            try:
                                lang_id = int(stringtable_string[:4], 16)
                                self.__versioninfo[u'Language'] = lang_id
                                self.__versioninfo[u'LanguageName'] = LANGUAGE.get(lang_id, (u'Unknow',))[0]
    #                            self.__versioninfo[u'PrimaryLanguage'] = LANG[lang_id & 0xff]
    #                            self.__versioninfo[u'SubLanguage'] = SUBLANG[(lang_id & 0xff00) >> 10]
                                codepage = int(stringtable_string[4:], 16)
                                self.__versioninfo[u'CodePage'] = codepage
                                self.__versioninfo[u'CodePageName'] = CODEPAGE.get(codepage, (u'Unknow',))[0]
                            except Exception, e:
                                self.__warnings.append(u'Versioninfo \'s language code is wrong, error: %s' % e)
                                print e
                        stringtable_struct.entries = {}
                        stringtable_struct.entries_offsets = {}
                        stringtable_struct.entries_lengths = {}
                        stringfileinfo_struct.StringTable.append(stringtable_struct)
                        entry_offset = self.dword_align(stringtable_offset + stringtable_struct.size +
                                2 * (len(stringtable_string) + 1), version_struct.OffsetToData)
                        # Process all entries in the string table
                        while entry_offset < stringtable_offset + stringtable_struct.Length:
                            string_struct = self.__unpack_data(self.String_FORMAT,
                                                                 file_offset = start_offset+entry_offset)
                            if not string_struct:
                                break
                            ustr_offset = version_struct.OffsetToData + entry_offset + string_struct.size
                            try:
                                key = self.get_string_u_at_rva(ustr_offset)
                                key_offset = self.get_offset_from_rva(ustr_offset)
                            except PEFormatError, e:
                                self.__warnings.append(
                                    u'Error parsing the version information, ' +
                                    u'attempting to read StringTable Key string. Can\'t ' +
                                    u'read unicode string at offset 0x%x' % (ustr_offset))
                                break
                            value_offset = self.dword_align(2*(len(key)+1) + entry_offset + string_struct.size,
                                version_struct.OffsetToData)
                            ustr_offset = version_struct.OffsetToData + value_offset
                            try:
                                value = self.get_string_u_at_rva(ustr_offset, max_length=string_struct.ValueLength).strip()
                                value_offset = self.get_offset_from_rva(ustr_offset)
                            except PEFormatError, e:
                                self.__warnings.append(
                                    u'Error parsing the version information, ' +
                                    u'attempting to read StringTable Value string. ' +
                                    u'Can\'t read unicode string at offset 0x%x' % (ustr_offset))
                                break
                            if string_struct.Length == 0:
                                entry_offset = stringtable_offset + stringtable_struct.Length
                            else:
                                entry_offset = self.dword_align(string_struct.Length+entry_offset, 
                                                                version_struct.OffsetToData)
                            key_as_char = []
                            for c in key:
                                if ord(c)>128:
                                    key_as_char.append('\\x%02x' % ord(c))
                                else:
                                    key_as_char.append(c)
                            key_as_char = ''.join(key_as_char)
#                            setattr(stringtable_struct, key_as_char, value)
                            setattr(stringtable_struct, key_as_char, value)
                            self.__versioninfo[key] = value
                            stringtable_struct.entries[key] = value
                            stringtable_struct.entries_offsets[key] = (key_offset, value_offset)
                            stringtable_struct.entries_lengths[key] = (len(key), len(value))
                        stringtable_offset = self.dword_align(
                            stringtable_struct.Length + stringtable_offset,
                            version_struct.OffsetToData)
                        if stringtable_offset >= stringfileinfo_struct.Length:
                            break
            # Parse a VarFileInfo entry
            
            elif stringfileinfo_string == u'VarFileInfo':
                varfileinfo_struct = stringfileinfo_struct
                varfileinfo_struct.name = u'VarFileInfo'
                if varfileinfo_struct.Type == 1 and varfileinfo_struct.ValueLength == 0:
                    var_offset = self.dword_align(
                        stringfileinfo_offset + varfileinfo_struct.size +
                            2 * (len(stringfileinfo_string) + 1),
                        version_struct.OffsetToData)
                    varfileinfo_struct.Var = []
                    # Process all entries
                    while True:
                        var_struct = self.__unpack_data(self.Var_FORMAT,
                            file_offset = start_offset+var_offset)
                        if not var_struct:
                            break
                        ustr_offset = (version_struct.OffsetToData + var_offset + 
                            var_struct.size)
                        try:
                            var_string = self.get_string_u_at_rva(ustr_offset)
                        except PEFormatError, excp:
                            self.__warnings.append(
                                u'Error parsing the version information, ' +
                                u'attempting to read VarFileInfo Var string. ' +
                                u'Can\'t read unicode string at offset 0x%x' % (ustr_offset))
                            break
                        varfileinfo_struct.Var.append(var_struct)
                        varword_offset = self.dword_align(
                            2 * (len(var_string) + 1) + var_offset + var_struct.size,
                            version_struct.OffsetToData)
                        orig_varword_offset = varword_offset
                        while varword_offset < orig_varword_offset + var_struct.ValueLength:
                            word1 = self.get_word_from_data(
                                raw_data[varword_offset:varword_offset+2], 0)
                            word2 = self.get_word_from_data(
                                raw_data[varword_offset+2:varword_offset+4], 0)
                            varword_offset += 4
                            if word1 is None:
                                word1 = 0
                            if word2 is None:
                                word2 = 0
                            var_struct.entry = {var_string: u'0x%04x 0x%04x' % (word1, word2)}
                        var_offset = self.dword_align(
                            var_offset+var_struct.Length, version_struct.OffsetToData)
                        if var_offset <= var_offset+var_struct.Length:
                            break
            # Increment and align the offset
            stringfileinfo_offset = self.dword_align(
                stringfileinfo_struct.Length+stringfileinfo_offset,
                version_struct.OffsetToData)
            # Check if all the StringFileInfo and VarFileInfo items have been processed
            if stringfileinfo_struct.Length == 0 or stringfileinfo_offset >= versioninfo_struct.Length:
                break

    def parse_export_directory(self, rva, size):
        """Parse the export directory.
        
        Given the rva of the export directory, it will process all
        its entries.
        
        The exports will be made available through a list "exports"
        containing a tuple with the following elements:
        
            (ordinal, symbol_address, symbol_name)
            
        And also through a dicionary "exports_by_ordinal" whose keys
        will be the ordinals and the values tuples of the from:
        
            (symbol_address, symbol_name)
            
        The symbol addresses are relative, not absolute.
        """
        try:
            export_dir =  self.__unpack_data(self.IMAGE_EXPORT_DIRECTORY_FORMAT,
                file_offset = self.get_offset_from_rva(rva))
        except PEFormatError:
            self.__warnings.append(u'Error parsing export directory at RVA: 0x%x' % (rva))
            return
        if not export_dir:
            return
        try:
            address_of_names = self.get_data(export_dir.AddressOfNames, export_dir.NumberOfNames*4)
            address_of_name_ordinals = self.get_data(export_dir.AddressOfNameOrdinals, export_dir.NumberOfNames*4)
            address_of_functions = self.get_data(export_dir.AddressOfFunctions, export_dir.NumberOfFunctions*4)
        except PEFormatError:
            self.__warnings.append(u'Error parsing export directory at RVA: 0x%x' % (rva))
            return
        exports = []
        try: # i don't know why NumberOfNames is so too long...
            funs_len = len(address_of_functions)
            for i in xrange(export_dir.NumberOfNames):
                symbol_name = self.get_string_at_rva(self.get_dword_from_data(address_of_names, i))
                symbol_ordinal = self.get_word_from_data(address_of_name_ordinals, i)
                if symbol_ordinal is not None and (symbol_ordinal * 4) < funs_len:
                    symbol_address = self.get_dword_from_data(address_of_functions, symbol_ordinal)
                else:
                    # Corrupt? a bad pointer... we assume it's all
                    # useless, no exports
                    return None
                # If the funcion's rva points within the export directory
                # it will point to a string with the forwarded symbol's string
                # instead of pointing the the function start address.
                if symbol_address >= rva and symbol_address < (rva + size):
                    forwarder_str = self.get_string_at_rva(symbol_address)
                else:
                    forwarder_str = None
                exports.append(ExportData(ordinal=export_dir.Base+symbol_ordinal,
                                          address=symbol_address,
                                          name=symbol_name,
                                          forwarder=forwarder_str))
        except OverflowError, e:
            self.__warnings.append(u'Error parsing export directory at RVA: 0x%x, error: %s' % (rva, e))
            return
        ordinals = [exp.ordinal for exp in exports]
        if export_dir.NumberOfFunctions > 100000: # I can't image how many functions it will be use.
            raise PEFormatError(u'ExportTable format error: number of functions is too many.')
        for idx in xrange(export_dir.NumberOfFunctions):
            if not idx+export_dir.Base in ordinals:
                symbol_address = self.get_dword_from_data(address_of_functions, idx)
                # Checking for forwarder again.
                if symbol_address>=rva and symbol_address<rva+size:
                    forwarder_str = self.get_string_at_rva(symbol_address)
                else:
                    forwarder_str = None
                exports.append(ExportData(ordinal=export_dir.Base+idx,
                                          address=symbol_address,
                                          name=None,
                                          forwarder=forwarder_str))
        return ExportDirData(struct=export_dir, symbols=exports)

    def dword_align(self, offset, base):
        offset += base
        return (offset + 3) - ((offset + 3) % 4) - base

    def parse_delay_import_directory(self, rva, size):
        """Walk and parse the delay import directory."""
        import_descs =  []
        while True:
#            try:
#                # If the RVA is invalid all would blow up. Some PEs seem to be
#                # specially nasty and have an invalid RVA.
#                data = self.get_data(rva)
#            except PEFormatError, e:
#                self.__warnings.append(u'Error parsing the Delay import directory at RVA: 0x%x' % (rva))
#                break
            import_desc = self.__unpack_data(self.IMAGE_DELAY_IMPORT_DESCRIPTOR_FORMAT,
                                                file_offset=self.get_offset_from_rva(rva))
            # If the structure is all zeores, we reached the end of the list
            if not import_desc or import_desc.all_zeroes:
                break
            rva += import_desc.size
            try:
                import_data = self.parse_imports(import_desc.pINT, import_desc.pIAT, None)
            except PEFormatError, e:
                self.__warnings.append(u'Error parsing the Delay import directory. ' +
                    u'Invalid import data at RVA: 0x%x' % ( rva ) )
                break
            if not import_data:
                continue
            dll = self.get_string_at_rva(import_desc.szName)
            if dll:
                import_descs.append(ImportDescData(struct=import_desc, imports=import_data, dll=dll))
        return import_descs

    def parse_import_directory(self, rva, size):
        """Walk and parse the import directory."""
        import_descs =  []
        import_data_none_count = 0 # if > 10, break
        while True:
#            try:
#                # If the RVA is invalid all would blow up. Some EXEs seem to be
#                # specially nasty and have an invalid RVA.
#                data = self.get_data(rva)
#            except PEFormatError, e:
#                self.__warnings.append(
#                    'Error parsing the Import directory at RVA: 0x%x' % ( rva ) )
#                break
            import_desc = self.__unpack_data(self.IMAGE_IMPORT_DESCRIPTOR_FORMAT,
                                                file_offset=self.get_offset_from_rva(rva))
            # If the structure is all zeores, we reached the end of the list
            if not import_desc or import_desc.all_zeroes:
                break
            rva += import_desc.size
            try:
                import_data = self.parse_imports(import_desc.OriginalFirstThunk,
                    import_desc.FirstThunk, import_desc.ForwarderChain)
            except PEFormatError, e:
                self.__warnings.append(u'Error parsing the Import directory. ' +
                    u'Invalid Import data at RVA: 0x%x, Error: %s' % (rva, unicode(e)))
                break
            if import_data is None:
                import_data_none_count += 1
                if import_data_none_count > 10:
                    break
                continue
            dll = self.get_string_at_rva(import_desc.Name)
            if dll:
                import_descs.append(ImportDescData(struct=import_desc, imports=import_data, dll=dll))
        return import_descs

    def parse_imports(self, original_first_thunk, first_thunk, forwarder_chain):
        """Parse the imported symbols.
        
        It will fill a list, which will be avalable as the dictionary
        attribute "imports". Its keys will be the DLL names and the values
        all the symbols imported from that object.
        """
        imported_symbols = []
        imports_section = self.get_section_by_rva(first_thunk)
        if not imports_section:
            raise PEFormatError(u'Invalid/corrupt imports.')
        # Import Lookup Table. Contains ordinals or pointers to strings.
        ilt = self.get_import_table(original_first_thunk)
        # Import Address Table. May have identical content to ILT if
        # PE file is not bounded, Will contain the address of the
        # imported symbols once the binary is loaded or if it is already
        # bound.
        iat = self.get_import_table(first_thunk)
        # OC Patch:
        # Would crash if iat or ilt had None type 
        if not iat and not ilt:
            raise PEFormatError(u'Invalid Import Table information. Both ILT and IAT appear to be broken.')
        if not iat and ilt:
            table = ilt
        elif iat and not ilt:
            table = iat
        elif ilt and ((len(ilt) and len(iat)==0) or (len(ilt) == len(iat))):
            table = ilt
        elif (ilt and len(ilt))==0 and (iat and len(iat)):
            table = iat
        else:
            return None
        for idx in xrange(len(table)):
            imp_ord = None
            imp_hint = None
            imp_name = None
            hint_name_table_rva = None
            if table[idx].AddressOfData:
                if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
                    ordinal_flag = IMAGE_ORDINAL_FLAG
                elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
                    ordinal_flag = IMAGE_ORDINAL_FLAG64
                # If imported by ordinal, we will append the ordinal number
                if table[idx].AddressOfData & ordinal_flag:
                    import_by_ordinal = True
                    imp_ord = table[idx].AddressOfData & 0xffff
                    imp_name = None
                else:
                    import_by_ordinal = False
                    try:
                        hint_name_table_rva = table[idx].AddressOfData & 0x7fffffff
                        data = self.get_data(hint_name_table_rva, 2)
                        # Get the Hint
                        imp_hint = self.get_word_from_data(data, 0)
                        imp_name = self.get_string_at_rva(table[idx].AddressOfData+2)
                    except PEFormatError, e:
                        pass
            imp_address = first_thunk + self.OPTIONAL_HEADER.ImageBase + idx * 4
            if iat and ilt and ilt[idx].AddressOfData != iat[idx].AddressOfData:
                imp_bound = iat[idx].AddressOfData
            else:
                imp_bound = None
            if imp_name != '' and (imp_ord or imp_name):
                imported_symbols.append(
                    ImportData(
                        import_by_ordinal=import_by_ordinal,
                        ordinal=imp_ord,
                        hint=imp_hint,
                        name=imp_name,
                        bound=imp_bound,
                        address=imp_address,
                        hint_name_table_rva=hint_name_table_rva))
        return imported_symbols

    def get_import_table(self, rva):
        table = []
        while True and rva:
#            try:
#                data = self.get_data(rva)
#            except PEFormatError, e:
#                self.__warnings.append(u'Error parsing the import table. ' +
#                    u'Invalid data at RVA: 0x%x' % ( rva ) )
#                return None
            if self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE:
                format = self.IMAGE_THUNK_DATA_FORMAT
            elif self.PE_TYPE == OPTIONAL_HEADER_MAGIC_PE_PLUS:
                format = self.IMAGE_THUNK_DATA64_FORMAT
            thunk_data = self.__unpack_data(format, file_offset=self.get_offset_from_rva(rva))
            if not thunk_data or thunk_data.all_zeroes:
                break
            rva += thunk_data.size
            table.append(thunk_data)
        return table

    def get_memory_mapped_image(self, max_virtual_address=0x10000000, ImageBase=None):
        """Returns the data corresponding to the memory layout of the PE file.
        
        The data includes the PE header and the sections loaded at offsets
        corresponding to their relative virtual addresses. (the VirtualAddress
        section header member).
        Any offset in this data corresponds to the absolute memory address
        ImageBase+offset.
        
        The optional argument 'max_virtual_address' provides with means of limiting
        which section are processed.
        Any section with their VirtualAddress beyond this value will be skipped.
        Normally, sections with values beyond this range are just there to confuse
        tools. It's a common trick to see in packed executables.
        
        If the 'ImageBase' optional argument is supplied, the file's relocations
        will be applied to the image by calling the 'relocate_image()' method.
        """
        # Collect all sections in one code block    
        data = self.header
        for section in self.sections:
            # Miscellanous integrity tests.
            # Some packer will set these to bogus values to
            # make tools go nuts.
            if section.Misc_VirtualSize == 0 or section.SizeOfRawData == 0:
                continue
            if section.SizeOfRawData > len(self.__data):
                continue
            if section.PointerToRawData > len(self.__data):
                continue
            if section.VirtualAddress >= max_virtual_address:
                continue
            padding_length = section.VirtualAddress - len(data)
            if padding_length > 0:
                data += '\0' * padding_length
            elif padding_length < 0:
                data = data[:padding_length]
            data += section.data
        return data

    def get_data(self, rva, length=None):
        """Get data regardless of the section where it lies on.
        
        Given a rva and the size of the chunk to retrieve, this method
        will find the section where the data lies and return the data.
        """
        s = self.get_section_by_rva(rva)
        if not s:
            if rva < len(self.header):
                if length:
                    end = rva + length
                else:
                    end = None
                return self.header[rva:end]
            raise PEFormatError(u'data at RVA can\'t be fetched. Corrupt header?')
        try:
            buf = s.get_data(rva, length)
            return buf
        except IndexError, e:
            print e
            raise PEFormatError(e)

    def get_rva_from_offset(self, offset):
        """Get the rva corresponding to this file offset. """
        s = self.get_section_by_offset(offset)
        if not s:
            raise PEFormatError("specified offset (0x%x) doesn't belong to any section." % offset)
        return s.get_rva_from_offset(offset)

    def get_offset_from_rva(self, rva):
        """Get the file offset corresponding to this rva.
        
        Given a rva , this method will find the section where the
        data lies and return the offset within the file.
        """
        s = self.get_section_by_rva(rva)
        if not s:
            raise PEFormatError, 'data at RVA can\'t be fetched. Corrupt header?'
        return s.get_offset_from_rva(rva)

    def get_string_at_rva(self, rva):
        """Get an ASCII string located at the given address."""
        s = self.get_section_by_rva(rva)
        if not s:
            if rva < len(self.header):
                return self.get_string_from_data(rva, self.header)
            return None
        return self.get_string_from_data(rva-s.VirtualAddress, s.data)
    
    def get_string_from_data(self, offset, data):
        """Get an ASCII string from within the data.
        return a unicode format string"""
        # OC Patch
        b = None
        try:
            b = data[offset]
            if not b:
                return ''
        except IndexError: # why offset will be str
            return ''
        except TypeError, e:
            raise PEFormatError(u'get_string_from_data: %s' % e)
        s = ''
        while ord(b):
            s += b
            offset += 1
            try:
                b = data[offset]
            except IndexError:
                break
            if not b:
                break
        return s
    
    def get_string_u_at_rva(self, rva, max_length = 2**16):
        """Get an Unicode string located at the given address."""
        try:
            # If the RVA is invalid all would blow up. Some EXEs seem to be
            # specially nasty and have an invalid RVA.
            data = self.get_data(rva, 2)
        except PEFormatError, e:
            return None
        #length = struct.unpack('<H', data)[0]
        s = u''
        for idx in xrange(max_length):
            try:
                uchr = struct.unpack('<H', self.get_data(rva+2*idx, 2))[0]
            except struct.error:
                break
            if unichr(uchr) == u'\0':
                break
            s += unichr(uchr)
        return s

    def get_section_by_offset(self, offset):
        """Get the section containing the given file offset."""
        sections = [s for s in self.sections if s.contains_offset(offset)]
        if sections:
            return sections[0]
        return None

    def get_section_by_rva(self, rva):
        """Get the section containing the given address."""
        try:
            sections = [s for s in self.sections if s.contains_rva(rva)]
        except Exception, e:
            raise PEFormatError(e)
        if sections:
            return sections[0]
        return None
    
    def __str__(self):
        return self.dump_info()
    
    def __unicode__(self):
        return self.dump_info()

    def print_info(self):
        """Print all the PE header information in a human readable from."""
        print self.dump_info()

    def dump_info(self, dump=None):
        """Dump all the PE header information into human readable string."""
        if dump is None:
            dump = Dump()
        if self.warnings:
            dump.add_header(u'Parsing Warnings')
            for warning in self.warnings:
                dump.add_line(warning)
                dump.add_newline()
        dump.add_header(u'DOS_HEADER')
        dump.add_lines(self.DOS_HEADER.dump())
        dump.add_newline()
        dump.add_header(u'NT_HEADERS')
        dump.add_lines(self.NT_HEADERS.dump())
        dump.add_newline()
        dump.add_header(u'FILE_HEADER')
        dump.add_lines(self.FILE_HEADER.dump())
        image_flags = self.retrieve_flags(IMAGE_CHARACTERISTICS, 'IMAGE_FILE_')
        dump.add(u'Flags: ')
        flags = []
        for flag in image_flags:
            if getattr(self.FILE_HEADER, flag[0]):
                flags.append(flag[0])
        dump.add_line(u', '.join(flags))
        dump.add_newline()
        if hasattr(self, u'OPTIONAL_HEADER') and self.OPTIONAL_HEADER is not None:
            dump.add_header(u'OPTIONAL_HEADER')
            dump.add_lines(self.OPTIONAL_HEADER.dump())
        dll_characteristics_flags = self.retrieve_flags(DLL_CHARACTERISTICS, 'IMAGE_DLL_CHARACTERISTICS_')
        dump.add(u'DllCharacteristics: ')
        flags = []
        for flag in dll_characteristics_flags:
            if getattr(self.OPTIONAL_HEADER, flag[0]):
                flags.append(flag[0])
        dump.add_line(u', '.join(flags))
        dump.add_newline()
        dump.add_header(u'PE Sections')
        section_flags = self.retrieve_flags(SECTION_CHARACTERISTICS, 'IMAGE_SCN_')
        for section in self.sections:
            dump.add_lines(section.dump())
            dump.add(u'Flags: ')
            flags = []
            for flag in section_flags:
                if getattr(section, flag[0]):
                    flags.append(flag[0])
            dump.add_line(u', '.join(flags))
#            dump.add_line(u'Entropy: %f (Min=0.0, Max=8.0)' % section.get_entropy())
            if md5 is not None:
                dump.add_line(u'MD5     hash: %s' % section.get_hash_md5())
            if sha1 is not None:
                dump.add_line(u'SHA-1   hash: %s' % section.get_hash_sha1())
            if sha256 is not None:
                dump.add_line(u'SHA-256 hash: %s' % section.get_hash_sha256())
            if sha512 is not None:
                dump.add_line(u'SHA-512 hash: %s' % section.get_hash_sha512())
            dump.add_newline()
        if (hasattr(self, u'OPTIONAL_HEADER') and 
            hasattr(self.OPTIONAL_HEADER, u'DATA_DIRECTORY')):
            dump.add_header(u'Directories')
            for idx in xrange(len(self.OPTIONAL_HEADER.DATA_DIRECTORY)):
                directory = self.OPTIONAL_HEADER.DATA_DIRECTORY[idx]
                dump.add_lines(directory.dump())
            dump.add_newline()
        
        if hasattr(self, u'VS_VERSIONINFO'):
            dump.add_header(u'Version Information')
            dump.add_lines(self.VS_VERSIONINFO.dump())
            dump.add_newline()

            if hasattr(self, u'VS_FIXEDFILEINFO'):
                dump.add_lines(self.VS_FIXEDFILEINFO.dump())
                dump.add_newline()

            if hasattr(self, u'FileInfo'):
                for entry in self.FileInfo:
                    dump.add_lines(entry.dump())
                    dump.add_newline()
                    
                    if hasattr(entry, u'StringTable'):
                        for st_entry in entry.StringTable:
                            [dump.add_line(u'  '+line) for line in st_entry.dump()]
                            dump.add_line(u'  LangID: '+st_entry.LangID)
                            dump.add_newline()
                            for str_entry in st_entry.entries.items():
                                dump.add_line(u'    %s: %s' % (str_entry[0], str_entry[1]))
                        dump.add_newline()
                    elif hasattr(entry, u'Var'):
                        for var_entry in entry.Var:
                            if hasattr(var_entry, u'entry'):
                                [dump.add_line(u'  '+line) for line in var_entry.dump()]
                                dump.add_line(u'    %s: %s' % \
                                            (var_entry.entry.keys()[0], var_entry.entry.values()[0]))
                        dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_EXPORT'):
            dump.add_header(u'Exported symbols')
            dump.add_lines(self.DIRECTORY_ENTRY_EXPORT.struct.dump())
            dump.add_newline()
            dump.add_line(u'%-10s   %-10s  %s' % (u'Ordinal', u'RVA', u'Name'))
            for export in self.DIRECTORY_ENTRY_EXPORT.symbols:
                dump.add(u'%-10d 0x%08Xh    %s' % (export.ordinal, export.address, export.name))
                if export.forwarder:
                    dump.add_line(u' forwarder: %s' % export.forwarder)
                else:
                    dump.add_newline()
            dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_IMPORT'):
            dump.add_header(u'Imported symbols')
            for module in self.DIRECTORY_ENTRY_IMPORT:
                dump.add_lines(module.struct.dump())
                dump.add_newline()
                for symbol in module.imports:
                    if symbol.import_by_ordinal is True:
                        dump.add(u'%s Ordinal[%s] (Imported by Ordinal)' % (module.dll, unicode(symbol.ordinal)))
                    else:
                        dump.add(u'%s.%s Hint[%s]' % (module.dll, symbol.name, unicode(symbol.hint)))
                    if symbol.bound:
                        dump.add_line(u' Bound: 0x%08X' % (symbol.bound))
                    else:
                        dump.add_newline()
                dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_BOUND_IMPORT'):
            dump.add_header(u'Bound imports')
            for bound_imp_desc in self.DIRECTORY_ENTRY_BOUND_IMPORT:
                dump.add_lines(bound_imp_desc.struct.dump())
                dump.add_line(u'DLL: %s' % bound_imp_desc.name)
                dump.add_newline()
                for bound_imp_ref in bound_imp_desc.entries:
                    dump.add_lines(bound_imp_ref.struct.dump(), 4)
                    dump.add_line(u'DLL: %s' % bound_imp_ref.name, 4)
                    dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_DELAY_IMPORT'):
            dump.add_header(u'Delay Imported symbols')
            for module in self.DIRECTORY_ENTRY_DELAY_IMPORT:
                dump.add_lines(module.struct.dump())
                dump.add_newline()
                for symbol in module.imports:
                    if symbol.import_by_ordinal is True:
                        dump.add(u'%s Ordinal[%s] (Imported by Ordinal)' % (module.dll, unicode(symbol.ordinal)))
                    else:
                        dump.add(u'%s.%s Hint[%s]' % (module.dll, symbol.name, unicode(symbol.hint)))
                    if symbol.bound:
                        dump.add_line(u' Bound: 0x%08X' % (symbol.bound))
                    else:
                        dump.add_newline()
                dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_RESOURCE'):
            dump.add_header(u'Resource directory')
            dump.add_lines(self.DIRECTORY_ENTRY_RESOURCE.struct.dump())
            for resource_type in self.DIRECTORY_ENTRY_RESOURCE.entries:
                if resource_type.name is not None:
                    dump.add_line(u'Name: [%s]' % resource_type.name, 2)
                else:
                    dump.add_line(u'Id: [0x%X] (%s)' % (
                        resource_type.struct.Id, RESOURCE_TYPE.get(resource_type.struct.Id, '-')), 2)
                dump.add_lines(resource_type.struct.dump(), 2)
                if hasattr(resource_type, u'directory'):
                    dump.add_lines(resource_type.directory.struct.dump(), 4)
                    for resource_id in resource_type.directory.entries:
                        if resource_id.name is not None:
                            dump.add_line(u'Name: [%s]' % resource_id.name, 6)
                        else:
                            dump.add_line(u'Id: [0x%X]' % resource_id.struct.Id, 6)
                        dump.add_lines(resource_id.struct.dump(), 6)
                        if hasattr(resource_id, u'directory'):
                            dump.add_lines(resource_id.directory.struct.dump(), 8)
                            for resource_lang in resource_id.directory.entries:
                            #    dump.add_line('\\--- LANG [%d,%d][%s]' % (
                            #        resource_lang.data.lang,
                            #        resource_lang.data.sublang,
                            #        LANG[resource_lang.data.lang]), 8)
                                dump.add_lines(resource_lang.struct.dump(), 10)
                                dump.add_lines(resource_lang.data.struct.dump(), 12)
                dump.add_newline()
            dump.add_newline()

        if (hasattr(self, u'DIRECTORY_ENTRY_TLS') 
                and self.DIRECTORY_ENTRY_TLS 
                and self.DIRECTORY_ENTRY_TLS.struct ):
            dump.add_header(u'TLS')
            dump.add_lines(self.DIRECTORY_ENTRY_TLS.struct.dump())
            dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_DEBUG'):
            dump.add_header(u'Debug information')
            for dbg in self.DIRECTORY_ENTRY_DEBUG:
                dump.add_lines(dbg.struct.dump())
                try:
                    dump.add_line(u'Type: %s' % DEBUG_TYPE[dbg.struct.Type])
                except KeyError:
                    dump.add_line(u'Type: 0x%x(Unknown)' % dbg.struct.Type)
                dump.add_newline()
        if hasattr(self, u'DIRECTORY_ENTRY_BASERELOC'):
            dump.add_header(u'Base relocations')
            for base_reloc in self.DIRECTORY_ENTRY_BASERELOC:
                dump.add_lines(base_reloc.struct.dump())
                for reloc in base_reloc.entries:
                    try:
                        dump.add_line(u'%08Xh %s' % (reloc.rva, RELOCATION_TYPE[reloc.type][16:]), 4)
                    except KeyError:
                        dump.add_line(u'0x%08X 0x%x(Unknown)' % (reloc.rva, reloc.type), 4)
                dump.add_newline()
        return dump.get_text()

    # OC Patch
    def get_physical_by_rva(self, rva):
        """Gets the physical address in the PE file from an RVA value."""
        try:
            return self.get_offset_from_rva(rva)
        except Exception:
            return None

    # Double-Word get/set
    def get_data_from_dword(self, dword):
        """Return a four byte string representing the double word value. (little endian)."""
        return struct.pack('<L', dword)

    def get_dword_from_data(self, data, offset):
        """Convert four bytes of data to a double word (little endian)
        
        'offset' is assumed to index into a dword array. So setting it to
        N will return a dword out of the data sarting at offset N*4.
        
        Returns None if the data can't be turned into a double word.
        """
        if (offset+1)*4 > len(data):
            return None
        return struct.unpack('<L', data[offset*4:(offset+1)*4])[0]

    def get_dword_at_rva(self, rva):
        """Return the double word value at the given RVA.
        
        Returns None if the value can't be read, i.e. the RVA can't be mapped
        to a file offset.
        """
        try:
            return self.get_dword_from_data(self.get_data(rva)[:4], 0)
        except PEFormatError:
            return None

    def get_dword_from_offset(self, offset):
        """Return the double word value at the given file offset. (little endian)"""
        if offset+4 > len(self.__data):
            return None
        return self.get_dword_from_data(self.__data[offset:offset+4], 0)

    def set_dword_at_rva(self, rva, dword):
        """Set the double word value at the file offset corresponding to the given RVA."""
        return self.set_bytes_at_rva(rva, self.get_data_from_dword(dword))
    
    def set_dword_at_offset(self, offset, dword):
        """Set the double word value at the given file offset."""
        return self.set_bytes_at_offset(offset, self.get_data_from_dword(dword))

    # Word get/set
    def get_data_from_word(self, word):
        """Return a two byte string representing the word value. (little endian)."""
        return struct.pack('<H', word)

    def get_word_from_data(self, data, offset):
        """Convert two bytes of data to a word (little endian)
        
        'offset' is assumed to index into a word array. So setting it to
        N will return a dword out of the data sarting at offset N*2.

        Returns None if the data can't be turned into a word.
        """
        if (offset+1) * 2 > len(data):
            return None
        return struct.unpack('<H', data[offset*2:(offset+1)*2])[0]

    def get_word_at_rva(self, rva):
        """Return the word value at the given RVA.
        
        Returns None if the value can't be read, i.e. the RVA can't be mapped
        to a file offset.
        """
        try:
            return self.get_word_from_data(self.get_data(rva)[:2], 0)
        except PEFormatError:
            return None

    def get_word_from_offset(self, offset):
        """Return the word value at the given file offset. (little endian)"""
        if (offset + 2) > len(self.__data):
            return None
        return self.get_word_from_data(self.__data[offset:offset+2], 0)

    def set_word_at_rva(self, rva, word):
        """Set the word value at the file offset corresponding to the given RVA."""
        return self.set_bytes_at_rva(rva, self.get_data_from_word(word))

    def set_word_at_offset(self, offset, word):
        """Set the word value at the given file offset."""
        return self.set_bytes_at_offset(offset, self.get_data_from_word(word))

    # Quad-Word get/set
    def get_data_from_qword(self, word):
        """Return a eight byte string representing the quad-word value. (little endian)."""
        return struct.pack('<Q', word)

    def get_qword_from_data(self, data, offset):
        """Convert eight bytes of data to a word (little endian)
        
        'offset' is assumed to index into a word array. So setting it to
        N will return a dword out of the data sarting at offset N*8.

        Returns None if the data can't be turned into a quad word.
        """
        if (offset + 1) * 8 > len(data):
            return None
        return struct.unpack('<Q', data[offset*8:(offset+1)*8])[0]

    def get_qword_at_rva(self, rva):
        """Return the quad-word value at the given RVA.
        
        Returns None if the value can't be read, i.e. the RVA can't be mapped
        to a file offset.
        """
        try:
            return self.get_qword_from_data(self.get_data(rva)[:8], 0)
        except PEFormatError:
            return None

    def get_qword_from_offset(self, offset):
        """Return the quad-word value at the given file offset. (little endian)"""
        if (offset + 8) > len(self.__data):
            return None
        return self.get_qword_from_data(self.__data[offset:offset+8], 0)

    def set_qword_at_rva(self, rva, qword):
        """Set the quad-word value at the file offset corresponding to the given RVA."""
        return self.set_bytes_at_rva(rva, self.get_data_from_qword(qword))

    def set_qword_at_offset(self, offset, qword):
        """Set the quad-word value at the given file offset."""
        return self.set_bytes_at_offset(offset, self.get_data_from_qword(qword))

    # Set bytes
    def set_bytes_at_rva(self, rva, data):
        """Overwrite, with the given string, the bytes at the file offset corresponding to the given RVA.
        
        Return True if successful, False otherwise. It can fail if the
        offset is outside the file's boundaries.
        """
        offset = self.get_physical_by_rva(rva)
        if not offset:
            raise False
        return self.set_bytes_at_offset(offset, data)

    def set_bytes_at_offset(self, offset, data):
        """Overwrite the bytes at the given file offset with the given string.
        
        Return True if successful, False otherwise. It can fail if the
        offset is outside the file's boundaries.
        """
        if not isinstance(data, str):
            raise TypeError(u'data should be of type: str')
        if offset >= 0 and offset < len(self.__data):
            self.__data = (self.__data[:offset] + data + self.__data[offset+len(data):])
        else:
            return False
        # Refresh the section's data with the modified information
        for section in self.sections:
            section_data_start = section.PointerToRawData
            section_data_end = section_data_start+section.SizeOfRawData
            section.data = self.__data[section_data_start:section_data_end]
        return True

    def relocate_image(self, new_ImageBase):
        """Apply the relocation information to the image using the provided new image base.
        
        This method will apply the relocation information to the image. Given the new base,
        all the relocations will be processed and both the raw data and the section's data
        will be fixed accordingly.
        The resulting image can be retrieved as well through the method:
        
            get_memory_mapped_image()
            
        In order to get something that would more closely match what could be found in memory
        once the Windows loader finished its work.
        """
        relocation_difference = new_ImageBase - self.OPTIONAL_HEADER.ImageBase
        for reloc in self.DIRECTORY_ENTRY_BASERELOC:
            virtual_address = reloc.struct.VirtualAddress
            size_of_block = reloc.struct.SizeOfBlock
            # We iterate with an index because if the relocation is of type
            # IMAGE_REL_BASED_HIGHADJ we need to also process the next entry
            # at once and skip it for the next interation
            entry_idx = 0
            while entry_idx < len(reloc.entries):
                entry = reloc.entries[entry_idx]
                entry_idx += 1
                if entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_ABSOLUTE']:
                    # Nothing to do for this type of relocation
                    pass
                elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGH']:
                    # Fix the high 16bits of a relocation
                    #
                    # Add high 16bits of relocation_difference to the
                    # 16bit value at RVA=entry.rva
                    self.set_word_at_rva(entry.rva,
                        (self.get_word_at_rva(entry.rva) + relocation_difference>>16)&0xffff)
                elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_LOW']:
                    # Fix the low 16bits of a relocation
                    #
                    # Add low 16 bits of relocation_difference to the 16bit value
                    # at RVA=entry.rva
                    self.set_word_at_rva(entry.rva, (self.get_word_at_rva(entry.rva) + relocation_difference) & 0xffff)
                elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGHLOW']:
                    # Handle all high and low parts of a 32bit relocation
                    #
                    # Add relocation_difference to the value at RVA=entry.rva
                    self.set_dword_at_rva(entry.rva, self.get_dword_at_rva(entry.rva)+relocation_difference)
                elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_HIGHADJ']:
                    # Fix the high 16bits of a relocation and adjust
                    #
                    # Add high 16bits of relocation_difference to the 32bit value
                    # composed from the (16bit value at RVA=entry.rva)<<16 plus 
                    # the 16bit value at the next relocation entry.
                    # If the next entry is beyond the array's limits,
                    # abort... the table is corrupt
                    if entry_idx == len(reloc.entries):
                        break
                    next_entry = reloc.entries[entry_idx]
                    entry_idx += 1
                    self.set_word_at_rva( entry.rva, 
                        ((self.get_word_at_rva(entry.rva)<<16) + next_entry.rva +
                        relocation_difference & 0xffff0000) >> 16)
                elif entry.type == RELOCATION_TYPE['IMAGE_REL_BASED_DIR64']:
                    # Apply the difference to the 64bit value at the offset
                    # RVA=entry.rva
                    self.set_qword_at_rva(entry.rva, self.get_qword_at_rva(entry.rva)+relocation_difference)

    def verify_checksum(self):
        return self.OPTIONAL_HEADER.CheckSum == self.generate_checksum()

    def generate_checksum(self):
        # Get the offset to the CheckSum field in the OptionalHeader
        checksum_offset = self.OPTIONAL_HEADER.__file_offset__ + 0x40 # 64
        checksum = 0
        for i in range(len(self.__data) / 4):
            # Skip the checksum field
            if i == checksum_offset / 4:
                continue
            dword = struct.unpack('L', self.__data[i*4:i*4+4])[0]
            checksum = (checksum & 0xffffffffL) + dword + (checksum >> 32)
            if checksum > 2 ** 32:
                checksum = (checksum & 0xffffffffL) + (checksum >> 32)
        checksum = (checksum & 0xffff) + (checksum >> 16)
        checksum = (checksum) + (checksum >> 16)
        checksum = checksum & 0xffff
        return checksum + len(self.__data)
    
    def __del__(self):
        if self.__fileobj is not None and not self.__fileobj.closed:
            self.__fileobj.close()
            self.__fileobj = None
    
    def close(self):
        self.__del__()
    
    @property
    def versioninfo(self):
        if self.__versioninfo is None:
            index = DIRECTORY_ENTRY[u'IMAGE_DIRECTORY_ENTRY_RESOURCE']
            try:
                dir_entry = self.OPTIONAL_HEADER.DATA_DIRECTORY[index]
            except IndexError, e:
                self.__versioninfo = None
            else:
                self.parse_resources_directory(dir_entry.VirtualAddress, dir_entry.Size)
            if self.__versioninfo is None:
                self.__versioninfo = {}
        return self.__versioninfo

def get_versioninfo(filepath):
    pe = PE(filepath, fast_load=True)
    return pe.versioninfo

def is_pe(filepath):
    """Check file is a PE or not
    >>> path = u'/media/softs__/softs/pes/'
    >>> is_pe(path + u'AdbeRdr810_zh_CN.exe')
    True
    >>> is_pe(path + u'AdbeRdr810_zh_CN.exe.txt')
    False
    """
    if not os.path.exists(filepath) or not os.path.isfile(filepath):
        return False
    size = os.path.getsize(filepath)
    if size == 0:
        return False
    try:
        fileobj = open(filepath, 'rb')
        data = Data(fileobj, size)

        dos_signature = struct.unpack('H', data[0:2])[0]
        if dos_signature != IMAGE_DOS_SIGNATURE:
            return False
        offset = struct.unpack('L', data[60:64])[0]
        signature = struct.unpack('L', data[offset:offset+4])[0]
        if signature == IMAGE_NT_SIGNATURE:
            return True
        else:
            signature = struct.unpack('H', data[offset:offset+2])[0]
            if signature in (IMAGE_OS2_SIGNATURE, IMAGE_OS2_SIGNATURE_LE):
                return True
        return False
    except struct.error:
        return False
    finally:
        fileobj.close()

def __doc_test():
    import doctest
    doctest.testmod()
    
from datetime import datetime
def test():
    path = u'e:/softs/pes/'
    for filename in os.listdir(path):
        filename = os.path.join(path, filename)
        if os.path.isdir(filename) or os.path.splitext(filename)[1] in (u'.txt', u'.xml'):
            continue
        print u'*' * 60
        print filename
        start = datetime.now()
#        flist = []
#        while True:
#            flist.append(open(filename, 'rb'))
#            print len(flist)
        pe = PE(filename)
        pe.parse()
        
        log = open(filename + u'.txt', 'wb')
        log.write(pe.dump_info().encode('utf-8'))
        log.close()
        if hasattr(pe, u'VS_FIXEDFILEINFO'):
            print pe.VS_FIXEDFILEINFO
        start = datetime.now()
        for key, value in pe.versioninfo.iteritems():
            print u'%s: %s' % (key, value)
        pe.close()
#    __doc_test()

if __name__ == '__main__':
    test()